From c4fdffe2c4116130d7344cd191744c6dc420495e Mon Sep 17 00:00:00 2001 From: Bryan Mishkin <698306+bmish@users.noreply.github.com> Date: Thu, 9 Dec 2021 08:47:05 -0500 Subject: [PATCH] chore: add prettier (#234) --- .eslintrc.js | 7 +- .prettierrc.js | 5 + build/generate-readme-table.js | 44 +- docs/rules/fixer-return.md | 12 +- docs/rules/meta-property-ordering.md | 7 +- docs/rules/no-deprecated-context-methods.md | 8 +- docs/rules/no-deprecated-report-api.md | 5 +- docs/rules/no-identical-tests.md | 10 +- docs/rules/no-missing-placeholders.md | 4 +- docs/rules/no-only-tests.md | 13 +- docs/rules/no-unused-placeholders.md | 4 +- docs/rules/no-useless-token-range.md | 4 +- docs/rules/prefer-message-ids.md | 8 +- docs/rules/prefer-object-rule.md | 58 +- docs/rules/prefer-output-null.md | 8 +- docs/rules/prefer-placeholders.md | 4 +- docs/rules/prefer-replace-text.md | 12 +- docs/rules/report-message-format.md | 10 +- docs/rules/require-meta-docs-description.md | 12 +- docs/rules/require-meta-docs-url.md | 29 +- docs/rules/require-meta-fixable.md | 16 +- docs/rules/require-meta-has-suggestions.md | 12 +- docs/rules/require-meta-schema.md | 18 +- docs/rules/require-meta-type.md | 6 +- docs/rules/test-case-property-ordering.md | 3 - docs/rules/test-case-shorthand-strings.md | 16 +- lib/index.js | 51 +- lib/rules/consistent-output.js | 23 +- lib/rules/fixer-return.js | 32 +- lib/rules/meta-property-ordering.js | 46 +- lib/rules/no-deprecated-context-methods.js | 51 +- lib/rules/no-deprecated-report-api.js | 29 +- lib/rules/no-identical-tests.js | 26 +- lib/rules/no-missing-placeholders.js | 47 +- lib/rules/no-only-tests.js | 32 +- lib/rules/no-unused-placeholders.js | 35 +- lib/rules/no-useless-token-range.js | 115 ++-- lib/rules/prefer-message-ids.js | 51 +- lib/rules/prefer-object-rule.js | 13 +- lib/rules/prefer-output-null.js | 34 +- lib/rules/prefer-placeholders.js | 22 +- lib/rules/prefer-replace-text.js | 37 +- lib/rules/report-message-format.js | 56 +- lib/rules/require-meta-docs-description.js | 44 +- lib/rules/require-meta-docs-url.js | 101 ++- lib/rules/require-meta-fixable.js | 80 ++- lib/rules/require-meta-has-suggestions.js | 98 ++- lib/rules/require-meta-schema.js | 53 +- lib/rules/require-meta-type.js | 19 +- lib/rules/test-case-property-ordering.js | 52 +- lib/rules/test-case-shorthand-strings.js | 123 ++-- lib/utils.js | 505 +++++++++------ package.json | 3 + tests/build/generate-readme-table.js | 5 +- tests/lib/index.js | 2 +- tests/lib/rule-setup.js | 97 ++- tests/lib/rules/fixer-return.js | 143 ++++- tests/lib/rules/meta-property-ordering.js | 34 +- .../rules/no-deprecated-context-methods.js | 15 +- tests/lib/rules/no-deprecated-report-api.js | 1 - tests/lib/rules/no-missing-placeholders.js | 11 +- tests/lib/rules/no-only-tests.js | 12 +- tests/lib/rules/no-unused-placeholders.js | 11 +- tests/lib/rules/no-useless-token-range.js | 30 +- tests/lib/rules/report-message-format.js | 17 +- .../rules/require-meta-docs-description.js | 29 +- tests/lib/rules/require-meta-docs-url.js | 308 +++++---- .../lib/rules/require-meta-has-suggestions.js | 132 +++- tests/lib/rules/require-meta-schema.js | 46 +- .../lib/rules/test-case-property-ordering.js | 35 +- .../lib/rules/test-case-shorthand-strings.js | 68 +- tests/lib/utils.js | 602 ++++++++++++------ 72 files changed, 2495 insertions(+), 1216 deletions(-) create mode 100644 .prettierrc.js diff --git a/.eslintrc.js b/.eslintrc.js index 2c7c8b2a..0c14d962 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,10 +2,15 @@ module.exports = { root: true, + parserOptions: { + ecmaVersion: 2021, + sourceType: 'script', + }, extends: [ 'not-an-aardvark/node', - 'plugin:unicorn/recommended', 'plugin:node/recommended', + 'plugin:prettier/recommended', + 'plugin:unicorn/recommended', ], rules: { 'comma-dangle': [ diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 00000000..534e6d35 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = { + singleQuote: true, +}; diff --git a/build/generate-readme-table.js b/build/generate-readme-table.js index 1cb49b9b..4f8084ff 100644 --- a/build/generate-readme-table.js +++ b/build/generate-readme-table.js @@ -10,35 +10,47 @@ const END_TABLE_MARKER = '\n'; const expectedTableLines = Object.keys(rules) .sort() - .reduce((lines, ruleId) => { - const rule = rules[ruleId]; - - lines.push([ - `[${ruleId}](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/${ruleId}.md)`, - rule.meta.docs.recommended ? '✔️' : '', - rule.meta.fixable ? '🛠' : '', - rule.meta.hasSuggestions ? '💡' : '', - rule.meta.docs.description, - ].join(' | ')); - - return lines; - }, ['Name | ✔️ | 🛠 | 💡 | Description', '----- | ----- | ----- | ----- | -----']) + .reduce( + (lines, ruleId) => { + const rule = rules[ruleId]; + + lines.push( + [ + `[${ruleId}](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/${ruleId}.md)`, + rule.meta.docs.recommended ? '✔️' : '', + rule.meta.fixable ? '🛠' : '', + rule.meta.hasSuggestions ? '💡' : '', + rule.meta.docs.description, + ].join(' | ') + ); + + return lines; + }, + [ + 'Name | ✔️ | 🛠 | 💡 | Description', + '----- | ----- | ----- | ----- | -----', + ] + ) .join('\n'); const readmeContents = fs.readFileSync(README_LOCATION, 'utf8'); if (!readmeContents.includes(BEGIN_TABLE_MARKER)) { - throw new Error(`Could not find '${BEGIN_TABLE_MARKER}' marker in README.md.`); + throw new Error( + `Could not find '${BEGIN_TABLE_MARKER}' marker in README.md.` + ); } if (!readmeContents.includes(END_TABLE_MARKER)) { throw new Error(`Could not find '${END_TABLE_MARKER}' marker in README.md.`); } -const linesStartIndex = readmeContents.indexOf(BEGIN_TABLE_MARKER) + BEGIN_TABLE_MARKER.length; +const linesStartIndex = + readmeContents.indexOf(BEGIN_TABLE_MARKER) + BEGIN_TABLE_MARKER.length; const linesEndIndex = readmeContents.indexOf(END_TABLE_MARKER); -const updatedReadmeContents = readmeContents.slice(0, linesStartIndex) + +const updatedReadmeContents = + readmeContents.slice(0, linesStartIndex) + expectedTableLines + readmeContents.slice(linesEndIndex); diff --git a/docs/rules/fixer-return.md b/docs/rules/fixer-return.md index 6926c327..4add8729 100644 --- a/docs/rules/fixer-return.md +++ b/docs/rules/fixer-return.md @@ -14,9 +14,9 @@ Examples of **incorrect** code for this rule: /* eslint eslint-plugin/fixer-return: error */ module.exports = { - create (context) { + create(context) { context.report({ - fix (fixer) { + fix(fixer) { fixer.insertTextAfter(node, 'foo'); }, }); @@ -30,9 +30,9 @@ Examples of **correct** code for this rule: /* eslint eslint-plugin/fixer-return: error */ module.exports = { - create (context) { + create(context) { context.report({ - fix (fixer) { + fix(fixer) { return fixer.insertTextAfter(node, 'foo'); }, }); @@ -44,9 +44,9 @@ module.exports = { /* eslint eslint-plugin/fixer-return: error */ module.exports = { - create (context) { + create(context) { context.report({ - fix (fixer) { + fix(fixer) { if (foo) { return; // no autofix in this situation } diff --git a/docs/rules/meta-property-ordering.md b/docs/rules/meta-property-ordering.md index 01b2ee9d..4493c243 100644 --- a/docs/rules/meta-property-ordering.md +++ b/docs/rules/meta-property-ordering.md @@ -15,7 +15,6 @@ This rule has an array option: Examples of **incorrect** code for this rule: ```js - /* eslint eslint-plugin/meta-property-ordering: ["error", ["type", "docs", "fixable", "schema", "messages"] ] */ @@ -27,7 +26,7 @@ module.exports = { type: 'problem', fixable: 'code', }, - create () {}, + create() {}, }; // invalid; extra properties must be placed afterwards. @@ -38,7 +37,7 @@ module.exports = { docs: '', fixable: 'code', }, - create () {}, + create() {}, }; ``` @@ -57,7 +56,7 @@ module.exports = { messages: ['zoo'], fooooooooo: 'foo', }, - create () {}, + create() {}, }; ``` diff --git a/docs/rules/no-deprecated-context-methods.md b/docs/rules/no-deprecated-context-methods.md index ee61dc85..e2bccfa2 100644 --- a/docs/rules/no-deprecated-context-methods.md +++ b/docs/rules/no-deprecated-context-methods.md @@ -37,9 +37,9 @@ Examples of **incorrect** code for this rule: ```js module.exports = { - create (context) { + create(context) { return { - Program (ast) { + Program(ast) { const firstToken = context.getFirstToken(ast); }, }; @@ -51,11 +51,11 @@ Examples of **correct** code for this rule: ```js module.exports = { - create (context) { + create(context) { const sourceCode = context.getSourceCode(); return { - Program (ast) { + Program(ast) { const firstToken = sourceCode.getFirstToken(ast); }, }; diff --git a/docs/rules/no-deprecated-report-api.md b/docs/rules/no-deprecated-report-api.md index 19231d59..92f06dff 100644 --- a/docs/rules/no-deprecated-report-api.md +++ b/docs/rules/no-deprecated-report-api.md @@ -19,18 +19,17 @@ Examples of **incorrect** code for this rule: ```js module.exports = { - create (context) { + create(context) { context.report(node, 'This node is bad.'); }, }; - ``` Examples of **correct** code for this rule: ```js module.exports = { - create (context) { + create(context) { context.report({ node, message: 'This node is bad.' }); context.report({ node, loc, message: 'This node is bad.' }); diff --git a/docs/rules/no-identical-tests.md b/docs/rules/no-identical-tests.md index 5893c2d5..f74519be 100644 --- a/docs/rules/no-identical-tests.md +++ b/docs/rules/no-identical-tests.md @@ -14,10 +14,7 @@ Examples of **incorrect** code for this rule: /* eslint eslint-plugin/no-identical-tests: error */ new RuleTester().run('foo', bar, { - valid: [ - { code: 'foo' }, - { code: 'foo' }, - ], + valid: [{ code: 'foo' }, { code: 'foo' }], invalid: [], }); ``` @@ -28,10 +25,7 @@ Examples of **correct** code for this rule: /* eslint eslint-plugin/no-identical-tests: error */ new RuleTester().run('foo', bar, { - valid: [ - { code: 'foo' }, - { code: 'bar' }, - ], + valid: [{ code: 'foo' }, { code: 'bar' }], invalid: [], }); ``` diff --git a/docs/rules/no-missing-placeholders.md b/docs/rules/no-missing-placeholders.md index 5a6e6142..df32525e 100644 --- a/docs/rules/no-missing-placeholders.md +++ b/docs/rules/no-missing-placeholders.md @@ -26,7 +26,7 @@ Examples of **incorrect** code for this rule: /* eslint eslint-plugin/no-missing-placeholders: error*/ module.exports = { - create (context) { + create(context) { context.report({ node, message: '{{something}} is wrong.', @@ -49,7 +49,7 @@ Examples of **correct** code for this rule: /* eslint eslint-plugin/no-missing-placeholders: error*/ module.exports = { - create (context) { + create(context) { context.report({ node, message: 'something is wrong.', diff --git a/docs/rules/no-only-tests.md b/docs/rules/no-only-tests.md index 8f950f78..e03be4af 100644 --- a/docs/rules/no-only-tests.md +++ b/docs/rules/no-only-tests.md @@ -30,7 +30,9 @@ ruleTester.run('my-rule', myRule, { { code: 'const invalid = 42;', only: true, - errors: [/* ... */], + errors: [ + /* ... */ + ], }, ], }); @@ -45,14 +47,13 @@ const { RuleTester } = require('eslint'); const ruleTester = new RuleTester(); ruleTester.run('my-rule', myRule, { - valid: [ - 'const valid = 42;', - { code: 'const valid = 42;' }, - ], + valid: ['const valid = 42;', { code: 'const valid = 42;' }], invalid: [ { code: 'const invalid = 42;', - errors: [/* ... */], + errors: [ + /* ... */ + ], }, ], }); diff --git a/docs/rules/no-unused-placeholders.md b/docs/rules/no-unused-placeholders.md index 00b96c64..67f7ae1c 100644 --- a/docs/rules/no-unused-placeholders.md +++ b/docs/rules/no-unused-placeholders.md @@ -14,7 +14,7 @@ Examples of **incorrect** code for this rule: /* eslint eslint-plugin/no-unused-placeholders: error*/ module.exports = { - create (context) { + create(context) { context.report({ node, message: 'something is wrong.', @@ -32,7 +32,7 @@ Examples of **correct** code for this rule: /* eslint eslint-plugin/no-unused-placeholders: error*/ module.exports = { - create (context) { + create(context) { context.report({ node, message: 'something is wrong.', diff --git a/docs/rules/no-useless-token-range.md b/docs/rules/no-useless-token-range.md index 6bd4de80..3593adca 100644 --- a/docs/rules/no-useless-token-range.md +++ b/docs/rules/no-useless-token-range.md @@ -16,7 +16,7 @@ Examples of **incorrect** code for this rule: /* eslint eslint-plugin/no-useless-token-range: error */ module.exports = { - create (context) { + create(context) { const sourceCode = context.getSourceCode(); const rangeStart = sourceCode.getFirstToken(node).range[0]; @@ -31,7 +31,7 @@ Examples of **correct** code for this rule: /* eslint eslint-plugin/no-useless-token-range: error */ module.exports = { - create (context) { + create(context) { const sourceCode = context.getSourceCode(); const rangeStart = node.range[0]; diff --git a/docs/rules/prefer-message-ids.md b/docs/rules/prefer-message-ids.md index 1c2ce5f1..25985332 100644 --- a/docs/rules/prefer-message-ids.md +++ b/docs/rules/prefer-message-ids.md @@ -15,9 +15,9 @@ Examples of **incorrect** code for this rule: /* eslint eslint-plugin/prefer-message-ids: error */ module.exports = { - create (context) { + create(context) { return { - CallExpression (node) { + CallExpression(node) { context.report({ node, message: 'Some error message.', @@ -39,9 +39,9 @@ module.exports = { someMessageId: 'Some error message', }, }, - create (context) { + create(context) { return { - CallExpression (node) { + CallExpression(node) { context.report({ node, messageId: 'someMessageId', diff --git a/docs/rules/prefer-object-rule.md b/docs/rules/prefer-object-rule.md index 11b3460d..cd853eed 100644 --- a/docs/rules/prefer-object-rule.md +++ b/docs/rules/prefer-object-rule.md @@ -14,21 +14,27 @@ Examples of **incorrect** code for this rule: /* eslint eslint-plugin/prefer-object-rule: error */ module.exports = function (context) { - return { Program () { - context.report(); - } }; + return { + Program() { + context.report(); + }, + }; }; -module.exports = function create (context) { - return { Program () { - context.report(); - } }; +module.exports = function create(context) { + return { + Program() { + context.report(); + }, + }; }; -module.exports = context => { - return { Program () { - context.report(); - } }; +module.exports = (context) => { + return { + Program() { + context.report(); + }, + }; }; ``` @@ -38,26 +44,32 @@ Examples of **correct** code for this rule: /* eslint eslint-plugin/prefer-object-rule: error */ module.exports = { - create (context) { - return { Program () { - context.report(); - } }; + create(context) { + return { + Program() { + context.report(); + }, + }; }, }; module.exports = { - create (context) { - return { Program () { - context.report(); - } }; + create(context) { + return { + Program() { + context.report(); + }, + }; }, }; module.exports = { - create: context => { - return { Program () { - context.report(); - } }; + create: (context) => { + return { + Program() { + context.report(); + }, + }; }, }; ``` diff --git a/docs/rules/prefer-output-null.md b/docs/rules/prefer-output-null.md index e45256d8..31d6ceb6 100644 --- a/docs/rules/prefer-output-null.md +++ b/docs/rules/prefer-output-null.md @@ -15,9 +15,7 @@ Examples of **incorrect** code for this rule: new RuleTester().run('foo', bar, { valid: [], - invalid: [ - { code: 'foo', output: 'foo', errors: [{ message: 'bar' }] }, - ], + invalid: [{ code: 'foo', output: 'foo', errors: [{ message: 'bar' }] }], }); ``` @@ -28,8 +26,6 @@ Examples of **correct** code for this rule: new RuleTester().run('foo', bar, { valid: [], - invalid: [ - { code: 'foo', output: null, errors: [{ message: 'bar' }] }, - ], + invalid: [{ code: 'foo', output: null, errors: [{ message: 'bar' }] }], }); ``` diff --git a/docs/rules/prefer-placeholders.md b/docs/rules/prefer-placeholders.md index e11f1c3b..3f855f33 100644 --- a/docs/rules/prefer-placeholders.md +++ b/docs/rules/prefer-placeholders.md @@ -25,7 +25,7 @@ Examples of **incorrect** code for this rule: /* eslint eslint-plugin/prefer-placeholders: error */ module.exports = { - create (context) { + create(context) { context.report({ node, message: `The node ${node.name} is not allowed to be used.`, @@ -45,7 +45,7 @@ Examples of **correct** code for this rule: /* eslint eslint-plugin/prefer-placeholders: error */ module.exports = { - create (context) { + create(context) { context.report({ node, message: 'The node {{name}} is not allowed to be used.', diff --git a/docs/rules/prefer-replace-text.md b/docs/rules/prefer-replace-text.md index 87d28551..df5ed146 100644 --- a/docs/rules/prefer-replace-text.md +++ b/docs/rules/prefer-replace-text.md @@ -10,9 +10,9 @@ Examples of **incorrect** code for this rule: /* eslint eslint-plugin/prefer-replace-text: error */ module.exports = { - create (context) { + create(context) { context.report({ - fix (fixer) { + fix(fixer) { // error, can be written: return fixer.replaceText([node, '']); return fixer.replaceTextRange([node.range[0], node.range[1]], ''); }, @@ -27,9 +27,9 @@ Examples of **correct** code for this rule: /* eslint eslint-plugin/prefer-replace-text: error */ module.exports = { - create (context) { + create(context) { context.report({ - fix (fixer) { + fix(fixer) { return fixer.replaceText(node, ''); }, }); @@ -37,9 +37,9 @@ module.exports = { }; module.exports = { - create (context) { + create(context) { context.report({ - fix (fixer) { + fix(fixer) { // start = ... // end = ... return fixer.replaceTextRange([start, end], ''); diff --git a/docs/rules/report-message-format.md b/docs/rules/report-message-format.md index 57451c52..4190fb98 100644 --- a/docs/rules/report-message-format.md +++ b/docs/rules/report-message-format.md @@ -32,12 +32,16 @@ Examples of **incorrect** code for this rule: module.exports = { meta: {}, - create (context) { + create(context) { context.report(node, 'this message does not match the regular expression.'); context.report(node, 'Neither does this one'); - context.report(node, 'This will get reported, regardless of the value of the {{placeholder}}', { placeholder: foo }); + context.report( + node, + 'This will get reported, regardless of the value of the {{placeholder}}', + { placeholder: foo } + ); }, }; ``` @@ -47,7 +51,7 @@ Examples of **correct** code for this rule: ```js module.exports = { meta: {}, - create (context) { + create(context) { context.report(node, 'This message matches the regular expression.'); context.report(node, 'So does this one.'); diff --git a/docs/rules/require-meta-docs-description.md b/docs/rules/require-meta-docs-description.md index 1ce3ccea..dce7facc 100644 --- a/docs/rules/require-meta-docs-description.md +++ b/docs/rules/require-meta-docs-description.md @@ -19,12 +19,16 @@ Examples of **incorrect** code for this rule: module.exports = { meta: {}, - create (context) {/* ... */}, + create(context) { + /* ... */ + }, }; module.exports = { meta: { description: 'this rule does ...' }, // missing allowed prefix - create (context) {/* ... */}, + create(context) { + /* ... */ + }, }; ``` @@ -35,7 +39,9 @@ Examples of **correct** code for this rule: module.exports = { meta: { description: 'disallow unused variables' }, - create (context) {/* ... */}, + create(context) { + /* ... */ + }, }; ``` diff --git a/docs/rules/require-meta-docs-url.md b/docs/rules/require-meta-docs-url.md index 1f0954e3..d6febe61 100644 --- a/docs/rules/require-meta-docs-url.md +++ b/docs/rules/require-meta-docs-url.md @@ -11,18 +11,15 @@ This rule aims to require ESLint rules to have a `meta.docs.url` property. Examples of **incorrect** code for this rule: ```js - /* eslint eslint-plugin/require-meta-docs-url: "error" */ module.exports = { meta: {}, - create (context) {}, + create(context) {}, }; - ``` ```js - /* eslint eslint-plugin/require-meta-docs-url: "error" */ module.exports = { @@ -31,13 +28,11 @@ module.exports = { url: undefined, }, }, - create (context) {}, + create(context) {}, }; - ``` ```js - /* eslint eslint-plugin/require-meta-docs-url: ["error", {"pattern": "path/to/{{name}}.md"}] */ module.exports = { @@ -46,15 +41,13 @@ module.exports = { url: 'wrong URL', }, }, - create (context) {}, + create(context) {}, }; - ``` Examples of **correct** code for this rule: ```js - /* eslint eslint-plugin/require-meta-docs-url: "error" */ module.exports = { @@ -63,13 +56,11 @@ module.exports = { url: 'a URL', }, }, - create (context) {}, + create(context) {}, }; - ``` ```js - /* eslint eslint-plugin/require-meta-docs-url: ["error", {"pattern": "path/to/{{name}}.md"}] */ module.exports = { @@ -78,9 +69,8 @@ module.exports = { url: 'path/to/rule-name.md', }, }, - create (context) {}, + create(context) {}, }; - ``` ## Options @@ -112,9 +102,12 @@ For example: module.exports = { plugins: ['eslint-plugin'], rules: { - 'eslint-plugin/require-meta-docs-url': ['error', { - pattern: `path/to/v${version}/docs/rules/{{name}}.md`, - }], + 'eslint-plugin/require-meta-docs-url': [ + 'error', + { + pattern: `path/to/v${version}/docs/rules/{{name}}.md`, + }, + ], }, }; ``` diff --git a/docs/rules/require-meta-fixable.md b/docs/rules/require-meta-fixable.md index ef0ba9ea..37ce7cb7 100644 --- a/docs/rules/require-meta-fixable.md +++ b/docs/rules/require-meta-fixable.md @@ -15,11 +15,11 @@ Examples of **incorrect** code for this rule: module.exports = { meta: {}, // missing `fixable` property - create (context) { + create(context) { context.report({ node, message: 'foo', - fix (fixer) { + fix(fixer) { return fixer.remove(node); }, }); @@ -32,11 +32,11 @@ module.exports = { module.exports = { meta: { fixable: 'not a valid meta.fixable value' }, - create (context) { + create(context) { context.report({ node, message: 'foo', - fix (fixer) { + fix(fixer) { return fixer.remove(node); }, }); @@ -49,7 +49,7 @@ module.exports = { module.exports = { meta: { fixable: 'code' }, // property enabled but no fixer detected - create (context) { + create(context) { context.report({ node, message: 'foo' }); }, }; @@ -62,11 +62,11 @@ Examples of **correct** code for this rule: module.exports = { meta: { fixable: 'code' }, - create (context) { + create(context) { context.report({ node, message: 'foo', - fix (fixer) { + fix(fixer) { return fixer.remove(node); }, }); @@ -79,7 +79,7 @@ module.exports = { module.exports = { meta: {}, - create (context) { + create(context) { context.report({ node, message: 'foo', diff --git a/docs/rules/require-meta-has-suggestions.md b/docs/rules/require-meta-has-suggestions.md index e5e2822f..3677db37 100644 --- a/docs/rules/require-meta-has-suggestions.md +++ b/docs/rules/require-meta-has-suggestions.md @@ -19,14 +19,14 @@ Examples of **incorrect** code for this rule: module.exports = { meta: {}, // Missing `meta.hasSuggestions`. - create (context) { + create(context) { context.report({ node, message: 'foo', suggest: [ { desc: 'Insert space at the beginning', - fix: fixer => fixer.insertTextBefore(node, ' '), + fix: (fixer) => fixer.insertTextBefore(node, ' '), }, ], }); @@ -39,7 +39,7 @@ module.exports = { module.exports = { meta: { hasSuggestions: true }, // Has `meta.hasSuggestions` enabled but never provides suggestions. - create (context) { + create(context) { context.report({ node, message: 'foo', @@ -55,14 +55,14 @@ Examples of **correct** code for this rule: module.exports = { meta: { hasSuggestions: true }, - create (context) { + create(context) { context.report({ node, message: 'foo', suggest: [ { desc: 'Insert space at the beginning', - fix: fixer => fixer.insertTextBefore(node, ' '), + fix: (fixer) => fixer.insertTextBefore(node, ' '), }, ], }); @@ -75,7 +75,7 @@ module.exports = { module.exports = { meta: {}, - create (context) { + create(context) { context.report({ node, message: 'foo', diff --git a/docs/rules/require-meta-schema.md b/docs/rules/require-meta-schema.md index 6caad978..ad8b9692 100644 --- a/docs/rules/require-meta-schema.md +++ b/docs/rules/require-meta-schema.md @@ -17,17 +17,21 @@ Examples of **incorrect** code for this rule: module.exports = { meta: {}, - create (context) {/* ... */}, + create(context) { + /* ... */ + }, }; module.exports = { meta: { schema: null }, - create (context) {/* ... */}, + create(context) { + /* ... */ + }, }; module.exports = { meta: { schema: [] }, - create (context) { + create(context) { const options = context.options; /* using options when schema is empty */ }, }; @@ -40,7 +44,9 @@ Examples of **correct** code for this rule: module.exports = { meta: { schema: [] }, // ensures no options are passed to the rule - create (context) {/* ... */}, + create(context) { + /* ... */ + }, }; module.exports = { @@ -57,7 +63,9 @@ module.exports = { }, ], }, - create (context) {/* ... */}, + create(context) { + /* ... */ + }, }; ``` diff --git a/docs/rules/require-meta-type.md b/docs/rules/require-meta-type.md index 6bc6164f..e72e1a60 100644 --- a/docs/rules/require-meta-type.md +++ b/docs/rules/require-meta-type.md @@ -21,14 +21,14 @@ Examples of **incorrect** code for this rule: module.exports = { meta: {}, - create (context) { + create(context) { // ... }, }; module.exports = { meta: { type: 'invalid' }, - create (context) { + create(context) { // ... }, }; @@ -41,7 +41,7 @@ Examples of **correct** code for this rule: module.exports = { meta: { type: 'problem' }, - create (context) { + create(context) { // ... }, }; diff --git a/docs/rules/test-case-property-ordering.md b/docs/rules/test-case-property-ordering.md index 4e62d531..f6838cd7 100644 --- a/docs/rules/test-case-property-ordering.md +++ b/docs/rules/test-case-property-ordering.md @@ -15,7 +15,6 @@ This rule has an array option: Examples of **incorrect** code for this rule: ```js - /* eslint eslint-plugin/test-case-property-ordering: ["error", ["code", "output", "options", "parserOptions", "errors"] ] */ @@ -27,7 +26,6 @@ const testCase1 = { output: 'bar', }; - // invalid; extra properties should need to be placed afterwards. const testCase2 = { code: 'foo', @@ -50,7 +48,6 @@ const testCase1 = { output: 'bar', options: ['baz'], }; - ``` ## When Not To Use It diff --git a/docs/rules/test-case-shorthand-strings.md b/docs/rules/test-case-shorthand-strings.md index bed82065..ae1bbe35 100644 --- a/docs/rules/test-case-shorthand-strings.md +++ b/docs/rules/test-case-shorthand-strings.md @@ -7,7 +7,6 @@ When writing valid test cases for rules with `RuleTester`, one can optionally in ```js ruleTester.run('example-rule', rule, { valid: [ - // shorthand string 'validTestCase;', @@ -83,10 +82,7 @@ Examples of **incorrect** code for this rule with the `never` option: /* eslint eslint-plugin/test-case-shorthand-strings: ["error", "never"] */ ruleTester.run('example-rule', rule, { - valid: [ - 'validTestCase;', - 'anotherValidTestCase;', - ], + valid: ['validTestCase;', 'anotherValidTestCase;'], invalid: [], }); ``` @@ -151,10 +147,7 @@ ruleTester.run('example-rule', rule, { }); ruleTester.run('example-rule', rule, { - valid: [ - 'validTestCase;', - 'anotherValidTestCase', - ], + valid: ['validTestCase;', 'anotherValidTestCase'], invalid: [], }); @@ -219,10 +212,7 @@ Examples of **correct** code for this rule with the `consistent-as-needed` optio /* eslint eslint-plugin/test-case-shorthand-strings: ["error", "consistent-as-needed"] */ ruleTester.run('example-rule', rule, { - valid: [ - 'validTestCase;', - 'anotherValidTestCase;', - ], + valid: ['validTestCase;', 'anotherValidTestCase;'], invalid: [], }); diff --git a/lib/index.js b/lib/index.js index 4ee38c96..a6892c22 100644 --- a/lib/index.js +++ b/lib/index.js @@ -16,11 +16,13 @@ const PLUGIN_NAME = packageMetadata.name.replace(/^eslint-plugin-/, ''); const configFilters = { all: () => true, - recommended: rule => rule.meta.docs.recommended, - rules: rule => rule.meta.docs.category === 'Rules', - tests: rule => rule.meta.docs.category === 'Tests', - 'rules-recommended': rule => configFilters.recommended(rule) && configFilters.rules(rule), - 'tests-recommended': rule => configFilters.recommended(rule) && configFilters.tests(rule), + recommended: (rule) => rule.meta.docs.recommended, + rules: (rule) => rule.meta.docs.category === 'Rules', + tests: (rule) => rule.meta.docs.category === 'Tests', + 'rules-recommended': (rule) => + configFilters.recommended(rule) && configFilters.rules(rule), + 'tests-recommended': (rule) => + configFilters.recommended(rule) && configFilters.tests(rule), }; // ------------------------------------------------------------------------------ @@ -28,23 +30,32 @@ const configFilters = { // ------------------------------------------------------------------------------ // import all rules in lib/rules -const allRules = Object.fromEntries(fs - .readdirSync(`${__dirname}/rules`) - .filter(fileName => fileName.endsWith('.js') && /^[^._]/.test(fileName)) - .map(fileName => fileName.replace(/\.js$/, '')) - .map(ruleName => [ruleName, require(path.join(__dirname, 'rules', ruleName))]) +const allRules = Object.fromEntries( + fs + .readdirSync(`${__dirname}/rules`) + .filter((fileName) => fileName.endsWith('.js') && /^[^._]/.test(fileName)) + .map((fileName) => fileName.replace(/\.js$/, '')) + .map((ruleName) => [ + ruleName, + require(path.join(__dirname, 'rules', ruleName)), + ]) ); module.exports.rules = allRules; // eslint-disable-next-line unicorn/prefer-object-from-entries -module.exports.configs = Object.keys(configFilters).reduce((configs, configName) => { - return Object.assign(configs, { - [configName]: { - plugins: ['eslint-plugin'], - rules: Object.fromEntries(Object.keys(allRules) - .filter(ruleName => configFilters[configName](allRules[ruleName])) - .map(ruleName => [`${PLUGIN_NAME}/${ruleName}`, 'error'])), - }, - }); -}, {}); +module.exports.configs = Object.keys(configFilters).reduce( + (configs, configName) => { + return Object.assign(configs, { + [configName]: { + plugins: ['eslint-plugin'], + rules: Object.fromEntries( + Object.keys(allRules) + .filter((ruleName) => configFilters[configName](allRules[ruleName])) + .map((ruleName) => [`${PLUGIN_NAME}/${ruleName}`, 'error']) + ), + }, + }); + }, + {} +); diff --git a/lib/rules/consistent-output.js b/lib/rules/consistent-output.js index d8078da0..18ce2824 100644 --- a/lib/rules/consistent-output.js +++ b/lib/rules/consistent-output.js @@ -16,7 +16,8 @@ module.exports = { meta: { type: 'suggestion', docs: { - description: 'enforce consistent use of `output` assertions in rule tests', + description: + 'enforce consistent use of `output` assertions in rule tests', category: 'Tests', recommended: true, url: 'https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/consistent-output.md', @@ -33,24 +34,28 @@ module.exports = { }, }, - create (context) { + create(context) { // ---------------------------------------------------------------------- // Public // ---------------------------------------------------------------------- const always = context.options[0] && context.options[0] === 'always'; return { - Program (ast) { - utils.getTestInfo(context, ast).forEach(testRun => { - const readableCases = testRun.invalid.filter(testCase => testCase.type === 'ObjectExpression'); - const casesWithoutOutput = readableCases - .filter(testCase => !testCase.properties.map(utils.getKeyName).includes('output')); + Program(ast) { + utils.getTestInfo(context, ast).forEach((testRun) => { + const readableCases = testRun.invalid.filter( + (testCase) => testCase.type === 'ObjectExpression' + ); + const casesWithoutOutput = readableCases.filter( + (testCase) => + !testCase.properties.map(utils.getKeyName).includes('output') + ); if ( - (casesWithoutOutput.length < readableCases.length) || + casesWithoutOutput.length < readableCases.length || (always && casesWithoutOutput.length > 0) ) { - casesWithoutOutput.forEach(testCase => { + casesWithoutOutput.forEach((testCase) => { context.report({ node: testCase, messageId: 'missingOutput', diff --git a/lib/rules/fixer-return.js b/lib/rules/fixer-return.js index 6226f1c0..fb092d67 100644 --- a/lib/rules/fixer-return.js +++ b/lib/rules/fixer-return.js @@ -33,7 +33,7 @@ module.exports = { }, }, - create (context) { + create(context) { let funcInfo = { upper: null, codePath: null, @@ -52,7 +52,10 @@ module.exports = { * @param {Location} loc - Optional location to report violation on. * @returns {void} */ - function ensureFunctionReturnedFix (node, loc = (node.id || node).loc.start) { + function ensureFunctionReturnedFix( + node, + loc = (node.id || node).loc.start + ) { if ( (node.generator && !funcInfo.hasYieldWithFixer) || // Generator function never yielded a fix (!node.generator && !funcInfo.hasReturnWithFixer) // Non-generator function never returned a fix @@ -72,7 +75,7 @@ module.exports = { * @param {Context} context * @returns {boolean} */ - function isFix (node) { + function isFix(node) { if (node.type === 'ArrayExpression' && node.elements.length === 0) { // An empty array is not a fix. return false; @@ -94,51 +97,56 @@ module.exports = { } return { - Program (ast) { + Program(ast) { const sourceCode = context.getSourceCode(); - contextIdentifiers = utils.getContextIdentifiers(sourceCode.scopeManager, ast); + contextIdentifiers = utils.getContextIdentifiers( + sourceCode.scopeManager, + ast + ); }, // Stacks this function's information. - onCodePathStart (codePath, node) { + onCodePathStart(codePath, node) { funcInfo = { upper: funcInfo, codePath, hasYieldWithFixer: false, hasReturnWithFixer: false, - shouldCheck: utils.isAutoFixerFunction(node, contextIdentifiers) || utils.isSuggestionFixerFunction(node, contextIdentifiers), + shouldCheck: + utils.isAutoFixerFunction(node, contextIdentifiers) || + utils.isSuggestionFixerFunction(node, contextIdentifiers), node, }; }, // Pops this function's information. - onCodePathEnd () { + onCodePathEnd() { funcInfo = funcInfo.upper; }, // Yield in generators - YieldExpression (node) { + YieldExpression(node) { if (funcInfo.shouldCheck && node.argument && isFix(node.argument)) { funcInfo.hasYieldWithFixer = true; } }, // Checks the return statement is valid. - ReturnStatement (node) { + ReturnStatement(node) { if (funcInfo.shouldCheck && node.argument && isFix(node.argument)) { funcInfo.hasReturnWithFixer = true; } }, // Ensure the current fixer function returned or yielded a fix. - 'FunctionExpression:exit' (node) { + 'FunctionExpression:exit'(node) { if (funcInfo.shouldCheck) { ensureFunctionReturnedFix(node); } }, // Ensure the current (arrow) fixer function returned a fix. - 'ArrowFunctionExpression:exit' (node) { + 'ArrowFunctionExpression:exit'(node) { if (funcInfo.shouldCheck) { const loc = context.getSourceCode().getTokenBefore(node.body).loc; // Show violation on arrow (=>). if (node.expression) { diff --git a/lib/rules/meta-property-ordering.js b/lib/rules/meta-property-ordering.js index 573e2465..2b8b4640 100644 --- a/lib/rules/meta-property-ordering.js +++ b/lib/rules/meta-property-ordering.js @@ -21,30 +21,36 @@ module.exports = { url: 'https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/meta-property-ordering.md', }, fixable: 'code', - schema: [{ - type: 'array', - elements: { type: 'string' }, - }], + schema: [ + { + type: 'array', + elements: { type: 'string' }, + }, + ], messages: { - inconsistentOrder: 'The meta properties should be placed in a consistent order: [{{order}}].', + inconsistentOrder: + 'The meta properties should be placed in a consistent order: [{{order}}].', }, }, - create (context) { + create(context) { const sourceCode = context.getSourceCode(); const info = getRuleInfo(sourceCode); - const order = context.options[0] || ['type', 'docs', 'fixable', 'hasSuggestions', 'schema', 'messages']; + const order = context.options[0] || [ + 'type', + 'docs', + 'fixable', + 'hasSuggestions', + 'schema', + 'messages', + ]; const orderMap = new Map(order.map((name, i) => [name, i])); return { - Program () { - if ( - !info || - !info.meta || - info.meta.properties.length < 2 - ) { + Program() { + if (!info || !info.meta || info.meta.properties.length < 2) { return; } @@ -52,7 +58,7 @@ module.exports = { let last; - const violatingProps = props.filter(prop => { + const violatingProps = props.filter((prop) => { const curr = orderMap.has(getKeyName(prop)) ? orderMap.get(getKeyName(prop)) : Number.POSITIVE_INFINITY; @@ -64,9 +70,13 @@ module.exports = { } const knownProps = props - .filter(prop => orderMap.has(getKeyName(prop))) - .sort((a, b) => orderMap.get(getKeyName(a)) - orderMap.get(getKeyName(b))); - const unknownProps = props.filter(prop => !orderMap.has(getKeyName(prop))); + .filter((prop) => orderMap.has(getKeyName(prop))) + .sort( + (a, b) => orderMap.get(getKeyName(a)) - orderMap.get(getKeyName(b)) + ); + const unknownProps = props.filter( + (prop) => !orderMap.has(getKeyName(prop)) + ); for (const violatingProp of violatingProps) { context.report({ @@ -75,7 +85,7 @@ module.exports = { data: { order: knownProps.map(getKeyName).join(', '), }, - fix (fixer) { + fix(fixer) { const expectedProps = [...knownProps, ...unknownProps]; return props.map((prop, k) => { return fixer.replaceText( diff --git a/lib/rules/no-deprecated-context-methods.js b/lib/rules/no-deprecated-context-methods.js index 9db3b5de..7ab07246 100644 --- a/lib/rules/no-deprecated-context-methods.js +++ b/lib/rules/no-deprecated-context-methods.js @@ -39,7 +39,8 @@ module.exports = { meta: { type: 'suggestion', docs: { - description: 'disallow usage of deprecated methods on rule context objects', + description: + 'disallow usage of deprecated methods on rule context objects', category: 'Rules', recommended: true, url: 'https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/no-deprecated-context-methods.md', @@ -47,11 +48,12 @@ module.exports = { fixable: 'code', schema: [], messages: { - newFormat: 'Use `{{contextName}}.getSourceCode().{{replacement}}` instead of `{{contextName}}.{{original}}`.', + newFormat: + 'Use `{{contextName}}.getSourceCode().{{replacement}}` instead of `{{contextName}}.{{original}}`.', }, }, - create (context) { + create(context) { const sourceCode = context.getSourceCode(); // ---------------------------------------------------------------------- @@ -59,29 +61,36 @@ module.exports = { // ---------------------------------------------------------------------- return { - 'Program:exit' (ast) { + 'Program:exit'(ast) { [...utils.getContextIdentifiers(sourceCode.scopeManager, ast)] .filter( - contextId => + (contextId) => contextId.parent.type === 'MemberExpression' && contextId === contextId.parent.object && contextId.parent.property.type === 'Identifier' && - Object.prototype.hasOwnProperty.call(DEPRECATED_PASSTHROUGHS, contextId.parent.property.name) - ).forEach( - contextId => - context.report({ - node: contextId.parent, - messageId: 'newFormat', - data: { - contextName: contextId.name, - original: contextId.parent.property.name, - replacement: DEPRECATED_PASSTHROUGHS[contextId.parent.property.name], - }, - fix: fixer => [ - fixer.insertTextAfter(contextId, '.getSourceCode()'), - fixer.replaceText(contextId.parent.property, DEPRECATED_PASSTHROUGHS[contextId.parent.property.name]), - ], - }) + Object.prototype.hasOwnProperty.call( + DEPRECATED_PASSTHROUGHS, + contextId.parent.property.name + ) + ) + .forEach((contextId) => + context.report({ + node: contextId.parent, + messageId: 'newFormat', + data: { + contextName: contextId.name, + original: contextId.parent.property.name, + replacement: + DEPRECATED_PASSTHROUGHS[contextId.parent.property.name], + }, + fix: (fixer) => [ + fixer.insertTextAfter(contextId, '.getSourceCode()'), + fixer.replaceText( + contextId.parent.property, + DEPRECATED_PASSTHROUGHS[contextId.parent.property.name] + ), + ], + }) ); }, }; diff --git a/lib/rules/no-deprecated-report-api.js b/lib/rules/no-deprecated-report-api.js index 47508538..735b46fd 100644 --- a/lib/rules/no-deprecated-report-api.js +++ b/lib/rules/no-deprecated-report-api.js @@ -16,7 +16,8 @@ module.exports = { meta: { type: 'suggestion', docs: { - description: 'disallow the version of `context.report()` with multiple arguments', + description: + 'disallow the version of `context.report()` with multiple arguments', category: 'Rules', recommended: true, url: 'https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/no-deprecated-report-api.md', @@ -28,7 +29,7 @@ module.exports = { }, }, - create (context) { + create(context) { const sourceCode = context.getSourceCode(); let contextIdentifiers; @@ -37,20 +38,26 @@ module.exports = { // ---------------------------------------------------------------------- return { - Program (ast) { - contextIdentifiers = utils.getContextIdentifiers(sourceCode.scopeManager, ast); + Program(ast) { + contextIdentifiers = utils.getContextIdentifiers( + sourceCode.scopeManager, + ast + ); }, - CallExpression (node) { + CallExpression(node) { if ( node.callee.type === 'MemberExpression' && contextIdentifiers.has(node.callee.object) && - node.callee.property.type === 'Identifier' && node.callee.property.name === 'report' && - (node.arguments.length > 1 || (node.arguments.length === 1 && node.arguments[0].type === 'SpreadElement')) + node.callee.property.type === 'Identifier' && + node.callee.property.name === 'report' && + (node.arguments.length > 1 || + (node.arguments.length === 1 && + node.arguments[0].type === 'SpreadElement')) ) { context.report({ node: node.callee.property, messageId: 'useNewAPI', - fix (fixer) { + fix(fixer) { const openingParen = sourceCode.getTokenBefore(node.arguments[0]); const closingParen = sourceCode.getLastToken(node); const reportInfo = utils.getReportInfo(node.arguments, context); @@ -61,7 +68,11 @@ module.exports = { return fixer.replaceTextRange( [openingParen.range[1], closingParen.range[0]], - `{${Object.keys(reportInfo).map(key => `${key}: ${sourceCode.getText(reportInfo[key])}`).join(', ')}}` + `{${Object.keys(reportInfo) + .map( + (key) => `${key}: ${sourceCode.getText(reportInfo[key])}` + ) + .join(', ')}}` ); }, }); diff --git a/lib/rules/no-identical-tests.js b/lib/rules/no-identical-tests.js index 2d07a898..dba22f80 100644 --- a/lib/rules/no-identical-tests.js +++ b/lib/rules/no-identical-tests.js @@ -28,7 +28,7 @@ module.exports = { }, }, - create (context) { + create(context) { // ---------------------------------------------------------------------- // Public // ---------------------------------------------------------------------- @@ -40,8 +40,8 @@ module.exports = { /** *compare two test cases despite of properties order. *@returns {boolean} if eq, return true, else return false. - */ - function eq (testA, testB) { + */ + function eq(testA, testB) { if (testA.type !== testB.type) { return false; } @@ -59,7 +59,7 @@ module.exports = { } const propertiesSetA = new Set(); - propertiesA.forEach(item => { + propertiesA.forEach((item) => { const code = sourceCode.getText(item); propertiesSetA.add(code); }); @@ -74,21 +74,25 @@ module.exports = { } return { - Program (ast) { - utils.getTestInfo(context, ast).forEach(testRun => { - [testRun.valid, testRun.invalid].forEach(tests => { + Program(ast) { + utils.getTestInfo(context, ast).forEach((testRun) => { + [testRun.valid, testRun.invalid].forEach((tests) => { const cache = []; - tests.forEach(test => { - if (cache.some(item => eq(item, test))) { + tests.forEach((test) => { + if (cache.some((item) => eq(item, test))) { context.report({ node: test, messageId: 'identical', - fix (fixer) { + fix(fixer) { const start = sourceCode.getTokenBefore(test); const end = sourceCode.getTokenAfter(test); return fixer.removeRange( // should remove test's trailing comma - [start.range[1], end.value === ',' ? end.range[1] : test.range[1]]); + [ + start.range[1], + end.value === ',' ? end.range[1] : test.range[1], + ] + ); }, }); } else { diff --git a/lib/rules/no-missing-placeholders.js b/lib/rules/no-missing-placeholders.js index 80ebb869..e598e882 100644 --- a/lib/rules/no-missing-placeholders.js +++ b/lib/rules/no-missing-placeholders.js @@ -25,11 +25,12 @@ module.exports = { fixable: null, schema: [], messages: { - placeholderDoesNotExist: 'The placeholder {{{{missingKey}}}} does not exist.', + placeholderDoesNotExist: + 'The placeholder {{{{missingKey}}}} does not exist.', }, }, - create (context) { + create(context) { let contextIdentifiers; // ---------------------------------------------------------------------- @@ -37,29 +38,38 @@ module.exports = { // ---------------------------------------------------------------------- return { - Program (ast) { + Program(ast) { const sourceCode = context.getSourceCode(); - contextIdentifiers = utils.getContextIdentifiers(sourceCode.scopeManager, ast); + contextIdentifiers = utils.getContextIdentifiers( + sourceCode.scopeManager, + ast + ); }, - CallExpression (node) { + CallExpression(node) { if ( node.callee.type === 'MemberExpression' && contextIdentifiers.has(node.callee.object) && - node.callee.property.type === 'Identifier' && node.callee.property.name === 'report' + node.callee.property.type === 'Identifier' && + node.callee.property.name === 'report' ) { const reportInfo = utils.getReportInfo(node.arguments, context); if (!reportInfo) { return; } - const reportMessagesAndDataArray = utils.collectReportViolationAndSuggestionData(reportInfo).filter(obj => obj.message); + const reportMessagesAndDataArray = utils + .collectReportViolationAndSuggestionData(reportInfo) + .filter((obj) => obj.message); for (const { message, data } of reportMessagesAndDataArray) { - const messageStaticValue = getStaticValue(message, context.getScope()); + const messageStaticValue = getStaticValue( + message, + context.getScope() + ); if ( - ( - (message.type === 'Literal' && typeof message.value === 'string') || - (messageStaticValue && typeof messageStaticValue.value === 'string') - ) && + ((message.type === 'Literal' && + typeof message.value === 'string') || + (messageStaticValue && + typeof messageStaticValue.value === 'string')) && (!data || data.type === 'ObjectExpression') ) { // Same regex as the one ESLint uses @@ -67,9 +77,16 @@ module.exports = { const PLACEHOLDER_MATCHER = /{{\s*([^{}]+?)\s*}}/g; let match; - while ((match = PLACEHOLDER_MATCHER.exec(message.value || messageStaticValue.value))) { // eslint-disable-line no-extra-parens - const matchingProperty = data && - data.properties.find(prop => utils.getKeyName(prop) === match[1]); + while ( + (match = PLACEHOLDER_MATCHER.exec( + message.value || messageStaticValue.value + )) + ) { + const matchingProperty = + data && + data.properties.find( + (prop) => utils.getKeyName(prop) === match[1] + ); if (!matchingProperty) { context.report({ diff --git a/lib/rules/no-only-tests.js b/lib/rules/no-only-tests.js index e6719bb2..caa646e3 100644 --- a/lib/rules/no-only-tests.js +++ b/lib/rules/no-only-tests.js @@ -1,7 +1,11 @@ 'use strict'; const utils = require('../utils'); -const { isCommaToken, isOpeningBraceToken, isClosingBraceToken } = require('eslint-utils'); +const { + isCommaToken, + isOpeningBraceToken, + isClosingBraceToken, +} = require('eslint-utils'); /** @type {import('eslint').Rule.RuleModule} */ module.exports = { @@ -22,16 +26,16 @@ module.exports = { }, }, - create (context) { + create(context) { return { - Program (ast) { + Program(ast) { for (const testRun of utils.getTestInfo(context, ast)) { for (const test of [...testRun.valid, ...testRun.invalid]) { if (test.type === 'ObjectExpression') { // Test case object: { code: 'const x = 123;', ... } const onlyProperty = test.properties.find( - property => + (property) => property.key.type === 'Identifier' && property.key.name === 'only' && property.value.type === 'Literal' && @@ -45,18 +49,26 @@ module.exports = { suggest: [ { messageId: 'removeOnly', - *fix (fixer) { + *fix(fixer) { const sourceCode = context.getSourceCode(); - const tokenBefore = sourceCode.getTokenBefore(onlyProperty); - const tokenAfter = sourceCode.getTokenAfter(onlyProperty); + const tokenBefore = + sourceCode.getTokenBefore(onlyProperty); + const tokenAfter = + sourceCode.getTokenAfter(onlyProperty); if ( - (isCommaToken(tokenBefore) && isCommaToken(tokenAfter)) || // In middle of properties - (isOpeningBraceToken(tokenBefore) && isCommaToken(tokenAfter)) // At beginning of properties + (isCommaToken(tokenBefore) && + isCommaToken(tokenAfter)) || // In middle of properties + (isOpeningBraceToken(tokenBefore) && + isCommaToken(tokenAfter)) // At beginning of properties ) { yield fixer.remove(tokenAfter); // Remove extra comma. } - if (isCommaToken(tokenBefore) && isClosingBraceToken(tokenAfter)) { // At end of properties + if ( + isCommaToken(tokenBefore) && + isClosingBraceToken(tokenAfter) + ) { + // At end of properties yield fixer.remove(tokenBefore); // Remove extra comma. } diff --git a/lib/rules/no-unused-placeholders.js b/lib/rules/no-unused-placeholders.js index 2ef7ad27..c552bfee 100644 --- a/lib/rules/no-unused-placeholders.js +++ b/lib/rules/no-unused-placeholders.js @@ -29,7 +29,7 @@ module.exports = { }, }, - create (context) { + create(context) { let contextIdentifiers; // ---------------------------------------------------------------------- @@ -37,29 +37,38 @@ module.exports = { // ---------------------------------------------------------------------- return { - Program (ast) { + Program(ast) { const sourceCode = context.getSourceCode(); - contextIdentifiers = utils.getContextIdentifiers(sourceCode.scopeManager, ast); + contextIdentifiers = utils.getContextIdentifiers( + sourceCode.scopeManager, + ast + ); }, - CallExpression (node) { + CallExpression(node) { if ( node.callee.type === 'MemberExpression' && - contextIdentifiers.has(node.callee.object) && - node.callee.property.type === 'Identifier' && node.callee.property.name === 'report' + contextIdentifiers.has(node.callee.object) && + node.callee.property.type === 'Identifier' && + node.callee.property.name === 'report' ) { const reportInfo = utils.getReportInfo(node.arguments, context); if (!reportInfo) { return; } - const reportMessagesAndDataArray = utils.collectReportViolationAndSuggestionData(reportInfo).filter(obj => obj.message); + const reportMessagesAndDataArray = utils + .collectReportViolationAndSuggestionData(reportInfo) + .filter((obj) => obj.message); for (const { message, data } of reportMessagesAndDataArray) { - const messageStaticValue = getStaticValue(message, context.getScope()); + const messageStaticValue = getStaticValue( + message, + context.getScope() + ); if ( - ( - (message.type === 'Literal' && typeof message.value === 'string') || - (messageStaticValue && typeof messageStaticValue.value === 'string') - ) && + ((message.type === 'Literal' && + typeof message.value === 'string') || + (messageStaticValue && + typeof messageStaticValue.value === 'string')) && data && data.type === 'ObjectExpression' ) { @@ -72,7 +81,7 @@ module.exports = { placeholdersInMessage.add(term); }); - data.properties.forEach(prop => { + data.properties.forEach((prop) => { const key = utils.getKeyName(prop); if (!placeholdersInMessage.has(key)) { context.report({ diff --git a/lib/rules/no-useless-token-range.js b/lib/rules/no-useless-token-range.js index 1208ef25..112d4c9f 100644 --- a/lib/rules/no-useless-token-range.js +++ b/lib/rules/no-useless-token-range.js @@ -16,7 +16,8 @@ module.exports = { meta: { type: 'suggestion', docs: { - description: 'disallow unnecessary calls to `sourceCode.getFirstToken()` and `sourceCode.getLastToken()`', + description: + 'disallow unnecessary calls to `sourceCode.getFirstToken()` and `sourceCode.getLastToken()`', category: 'Rules', recommended: true, url: 'https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/no-useless-token-range.md', @@ -28,7 +29,7 @@ module.exports = { }, }, - create (context) { + create(context) { const sourceCode = context.getSourceCode(); // ---------------------------------------------------------------------- @@ -41,18 +42,19 @@ module.exports = { * @param {ASTNode} arg The second argument to `sourceCode.getFirstToken()` or `sourceCode.getLastToken()` * @returns {boolean} `true` if the argument affects the output of getFirstToken or getLastToken */ - function affectsGetTokenOutput (arg) { + function affectsGetTokenOutput(arg) { if (!arg) { return false; } if (arg.type !== 'ObjectExpression') { return true; } - return arg.properties.length >= 2 || ( - arg.properties.length === 1 && ( - utils.getKeyName(arg.properties[0]) !== 'includeComments' || - arg.properties[0].value.type !== 'Literal' - )); + return ( + arg.properties.length >= 2 || + (arg.properties.length === 1 && + (utils.getKeyName(arg.properties[0]) !== 'includeComments' || + arg.properties[0].value.type !== 'Literal')) + ); } /** @@ -60,8 +62,12 @@ module.exports = { * @param {ASTNode} node The node * @returns {boolean} `true` if the node is a MemberExpression that accesses the `range` property */ - function isRangeAccess (node) { - return node.type === 'MemberExpression' && node.property.type === 'Identifier' && node.property.name === 'range'; + function isRangeAccess(node) { + return ( + node.type === 'MemberExpression' && + node.property.type === 'Identifier' && + node.property.name === 'range' + ); } /** @@ -70,16 +76,20 @@ module.exports = { * @param {ASTNode} memberExpression The MemberExpression node to check * @returns {boolean} `true` if this node accesses either `.range[0]` or `.start` */ - function isStartAccess (memberExpression) { - if (isRangeAccess(memberExpression) && memberExpression.parent.type === 'MemberExpression') { + function isStartAccess(memberExpression) { + if ( + isRangeAccess(memberExpression) && + memberExpression.parent.type === 'MemberExpression' + ) { return isStartAccess(memberExpression.parent); } return ( - (memberExpression.property.type === 'Identifier' && memberExpression.property.name === 'start') || - ( - memberExpression.computed && memberExpression.property.type === 'Literal' && memberExpression.property.value === 0 && - isRangeAccess(memberExpression.object) - ) + (memberExpression.property.type === 'Identifier' && + memberExpression.property.name === 'start') || + (memberExpression.computed && + memberExpression.property.type === 'Literal' && + memberExpression.property.value === 0 && + isRangeAccess(memberExpression.object)) ); } @@ -89,16 +99,20 @@ module.exports = { * @param {ASTNode} memberExpression The MemberExpression node to check * @returns {boolean} `true` if this node accesses either `.range[1]` or `.end` */ - function isEndAccess (memberExpression) { - if (isRangeAccess(memberExpression) && memberExpression.parent.type === 'MemberExpression') { + function isEndAccess(memberExpression) { + if ( + isRangeAccess(memberExpression) && + memberExpression.parent.type === 'MemberExpression' + ) { return isEndAccess(memberExpression.parent); } return ( - (memberExpression.property.type === 'Identifier' && memberExpression.property.name === 'end') || - ( - memberExpression.computed && memberExpression.property.type === 'Literal' && memberExpression.property.value === 1 && - isRangeAccess(memberExpression.object) - ) + (memberExpression.property.type === 'Identifier' && + memberExpression.property.name === 'end') || + (memberExpression.computed && + memberExpression.property.type === 'Literal' && + memberExpression.property.value === 1 && + isRangeAccess(memberExpression.object)) ); } @@ -107,33 +121,50 @@ module.exports = { // ---------------------------------------------------------------------- return { - 'Program:exit' (ast) { + 'Program:exit'(ast) { [...utils.getSourceCodeIdentifiers(sourceCode.scopeManager, ast)] - .filter(identifier => identifier.parent.type === 'MemberExpression' && - identifier.parent.object === identifier && - identifier.parent.property.type === 'Identifier' && - identifier.parent.parent.type === 'CallExpression' && - identifier.parent === identifier.parent.parent.callee && - identifier.parent.parent.arguments.length <= 2 && - !affectsGetTokenOutput(identifier.parent.parent.arguments[1]) && - identifier.parent.parent.parent.type === 'MemberExpression' && - identifier.parent.parent === identifier.parent.parent.parent.object && ( - (isStartAccess(identifier.parent.parent.parent) && identifier.parent.property.name === 'getFirstToken') || - (isEndAccess(identifier.parent.parent.parent) && identifier.parent.property.name === 'getLastToken')) + .filter( + (identifier) => + identifier.parent.type === 'MemberExpression' && + identifier.parent.object === identifier && + identifier.parent.property.type === 'Identifier' && + identifier.parent.parent.type === 'CallExpression' && + identifier.parent === identifier.parent.parent.callee && + identifier.parent.parent.arguments.length <= 2 && + !affectsGetTokenOutput(identifier.parent.parent.arguments[1]) && + identifier.parent.parent.parent.type === 'MemberExpression' && + identifier.parent.parent === + identifier.parent.parent.parent.object && + ((isStartAccess(identifier.parent.parent.parent) && + identifier.parent.property.name === 'getFirstToken') || + (isEndAccess(identifier.parent.parent.parent) && + identifier.parent.property.name === 'getLastToken')) ) - .forEach(identifier => { - const fullRangeAccess = isRangeAccess(identifier.parent.parent.parent) + .forEach((identifier) => { + const fullRangeAccess = isRangeAccess( + identifier.parent.parent.parent + ) ? identifier.parent.parent.parent.parent : identifier.parent.parent.parent; - const replacementText = sourceCode.text.slice(fullRangeAccess.range[0], identifier.parent.parent.range[0]) + + const replacementText = + sourceCode.text.slice( + fullRangeAccess.range[0], + identifier.parent.parent.range[0] + ) + sourceCode.getText(identifier.parent.parent.arguments[0]) + - sourceCode.text.slice(identifier.parent.parent.range[1], fullRangeAccess.range[1]); + sourceCode.text.slice( + identifier.parent.parent.range[1], + fullRangeAccess.range[1] + ); context.report({ node: identifier.parent.parent, messageId: 'useReplacement', data: { replacementText }, - fix (fixer) { - return fixer.replaceText(identifier.parent.parent, sourceCode.getText(identifier.parent.parent.arguments[0])); + fix(fixer) { + return fixer.replaceText( + identifier.parent.parent, + sourceCode.getText(identifier.parent.parent.arguments[0]) + ); }, }); }); diff --git a/lib/rules/prefer-message-ids.js b/lib/rules/prefer-message-ids.js index 6fe03221..9fca98a5 100644 --- a/lib/rules/prefer-message-ids.js +++ b/lib/rules/prefer-message-ids.js @@ -12,7 +12,8 @@ module.exports = { meta: { type: 'problem', docs: { - description: 'require using `messageId` instead of `message` to report rule violations', + description: + 'require using `messageId` instead of `message` to report rule violations', category: 'Rules', recommended: false, url: 'https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/prefer-message-ids.md', @@ -20,12 +21,13 @@ module.exports = { fixable: null, schema: [], messages: { - messagesMissing: '`meta.messages` must contain at least one violation message.', + messagesMissing: + '`meta.messages` must contain at least one violation message.', foundMessage: 'Use `messageId` instead of `message`.', }, }, - create (context) { + create(context) { const sourceCode = context.getSourceCode(); const info = utils.getRuleInfo(sourceCode); @@ -36,8 +38,11 @@ module.exports = { // ---------------------------------------------------------------------- return { - Program (ast) { - contextIdentifiers = utils.getContextIdentifiers(sourceCode.scopeManager, ast); + Program(ast) { + contextIdentifiers = utils.getContextIdentifiers( + sourceCode.scopeManager, + ast + ); if (info === null) { return; @@ -47,34 +52,52 @@ module.exports = { const messagesNode = metaNode && metaNode.properties && - metaNode.properties.find(p => p.type === 'Property' && utils.getKeyName(p) === 'messages'); + metaNode.properties.find( + (p) => p.type === 'Property' && utils.getKeyName(p) === 'messages' + ); if (!messagesNode) { - context.report({ node: metaNode || info.create, messageId: 'messagesMissing' }); + context.report({ + node: metaNode || info.create, + messageId: 'messagesMissing', + }); return; } - const staticValue = getStaticValue(messagesNode.value, context.getScope()); + const staticValue = getStaticValue( + messagesNode.value, + context.getScope() + ); if (!staticValue) { return; } - if (typeof staticValue.value === 'object' && staticValue.value.constructor === Object && Object.keys(staticValue.value).length === 0) { - context.report({ node: messagesNode.value, messageId: 'messagesMissing' }); + if ( + typeof staticValue.value === 'object' && + staticValue.value.constructor === Object && + Object.keys(staticValue.value).length === 0 + ) { + context.report({ + node: messagesNode.value, + messageId: 'messagesMissing', + }); } }, - CallExpression (node) { + CallExpression(node) { if ( node.callee.type === 'MemberExpression' && - contextIdentifiers.has(node.callee.object) && - node.callee.property.type === 'Identifier' && node.callee.property.name === 'report' + contextIdentifiers.has(node.callee.object) && + node.callee.property.type === 'Identifier' && + node.callee.property.name === 'report' ) { const reportInfo = utils.getReportInfo(node.arguments, context); if (!reportInfo) { return; } - const reportMessagesAndDataArray = utils.collectReportViolationAndSuggestionData(reportInfo).filter(obj => obj.message); + const reportMessagesAndDataArray = utils + .collectReportViolationAndSuggestionData(reportInfo) + .filter((obj) => obj.message); for (const { message } of reportMessagesAndDataArray) { context.report({ node: message.parent, diff --git a/lib/rules/prefer-object-rule.js b/lib/rules/prefer-object-rule.js index cab41879..bb773b0a 100644 --- a/lib/rules/prefer-object-rule.js +++ b/lib/rules/prefer-object-rule.js @@ -27,7 +27,7 @@ module.exports = { }, }, - create (context) { + create(context) { // ---------------------------------------------------------------------- // Public // ---------------------------------------------------------------------- @@ -36,7 +36,7 @@ module.exports = { const ruleInfo = utils.getRuleInfo(sourceCode); return { - Program () { + Program() { if (!ruleInfo || ruleInfo.isNewStyle) { return; } @@ -44,14 +44,17 @@ module.exports = { context.report({ node: ruleInfo.create, messageId: 'preferObject', - *fix (fixer) { + *fix(fixer) { // note - we intentionally don't worry about formatting here, as otherwise we have // to indent the function correctly - if (ruleInfo.create.type === 'FunctionExpression' || ruleInfo.create.type === 'FunctionDeclaration') { + if ( + ruleInfo.create.type === 'FunctionExpression' || + ruleInfo.create.type === 'FunctionDeclaration' + ) { const openParenToken = sourceCode.getFirstToken( ruleInfo.create, - token => token.type === 'Punctuator' && token.value === '(' + (token) => token.type === 'Punctuator' && token.value === '(' ); /* istanbul ignore if */ diff --git a/lib/rules/prefer-output-null.js b/lib/rules/prefer-output-null.js index b40e23ad..87fe877f 100644 --- a/lib/rules/prefer-output-null.js +++ b/lib/rules/prefer-output-null.js @@ -16,7 +16,8 @@ module.exports = { meta: { type: 'suggestion', docs: { - description: 'disallow invalid RuleTester test cases where the `output` matches the `code`', + description: + 'disallow invalid RuleTester test cases where the `output` matches the `code`', category: 'Tests', recommended: false, url: 'https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/prefer-output-null.md', @@ -24,11 +25,12 @@ module.exports = { fixable: 'code', schema: [], messages: { - useOutputNull: 'Use `output: null` to assert that a test case is not autofixed.', + useOutputNull: + 'Use `output: null` to assert that a test case is not autofixed.', }, }, - create (context) { + create(context) { // ---------------------------------------------------------------------- // Public // ---------------------------------------------------------------------- @@ -36,17 +38,17 @@ module.exports = { const sourceCode = context.getSourceCode(); return { - Program (ast) { - utils.getTestInfo(context, ast).forEach(testRun => { - testRun.invalid.forEach(test => { + Program(ast) { + utils.getTestInfo(context, ast).forEach((testRun) => { + testRun.invalid.forEach((test) => { /** - * Get a test case's giving keyname node. - * @param {string} the keyname to find. - * @returns {Node} found node; if not found, return null; - */ - function getTestInfo (key) { + * Get a test case's giving keyname node. + * @param {string} the keyname to find. + * @returns {Node} found node; if not found, return null; + */ + function getTestInfo(key) { if (test.type === 'ObjectExpression') { - return test.properties.find(item => item.key.name === key); + return test.properties.find((item) => item.key.name === key); } return null; } @@ -54,11 +56,15 @@ module.exports = { const code = getTestInfo('code'); const output = getTestInfo('output'); - if (output && sourceCode.getText(output.value) === sourceCode.getText(code.value)) { + if ( + output && + sourceCode.getText(output.value) === + sourceCode.getText(code.value) + ) { context.report({ node: output, messageId: 'useOutputNull', - fix: fixer => fixer.replaceText(output.value, 'null'), + fix: (fixer) => fixer.replaceText(output.value, 'null'), }); } }); diff --git a/lib/rules/prefer-placeholders.js b/lib/rules/prefer-placeholders.js index 8edd340d..05819f93 100644 --- a/lib/rules/prefer-placeholders.js +++ b/lib/rules/prefer-placeholders.js @@ -25,11 +25,12 @@ module.exports = { fixable: null, schema: [], messages: { - usePlaceholders: 'Use report message placeholders instead of string concatenation.', + usePlaceholders: + 'Use report message placeholders instead of string concatenation.', }, }, - create (context) { + create(context) { let contextIdentifiers; const sourceCode = context.getSourceCode(); @@ -40,14 +41,15 @@ module.exports = { // ---------------------------------------------------------------------- return { - Program (ast) { + Program(ast) { contextIdentifiers = utils.getContextIdentifiers(scopeManager, ast); }, - CallExpression (node) { + CallExpression(node) { if ( node.callee.type === 'MemberExpression' && contextIdentifiers.has(node.callee.object) && - node.callee.property.type === 'Identifier' && node.callee.property.name === 'report' + node.callee.property.type === 'Identifier' && + node.callee.property.name === 'report' ) { const reportInfo = utils.getReportInfo(node.arguments, context); @@ -55,7 +57,9 @@ module.exports = { return; } - const reportMessagesAndDataArray = utils.collectReportViolationAndSuggestionData(reportInfo).filter(obj => obj.message); + const reportMessagesAndDataArray = utils + .collectReportViolationAndSuggestionData(reportInfo) + .filter((obj) => obj.message); for (let { message: messageNode } of reportMessagesAndDataArray) { if (messageNode.type === 'Identifier') { // See if we can find the variable declaration. @@ -80,8 +84,10 @@ module.exports = { } if ( - (messageNode.type === 'TemplateLiteral' && messageNode.expressions.length > 0) || - (messageNode.type === 'BinaryExpression' && messageNode.operator === '+') + (messageNode.type === 'TemplateLiteral' && + messageNode.expressions.length > 0) || + (messageNode.type === 'BinaryExpression' && + messageNode.operator === '+') ) { context.report({ node: messageNode, diff --git a/lib/rules/prefer-replace-text.js b/lib/rules/prefer-replace-text.js index 9b51000f..4859a879 100644 --- a/lib/rules/prefer-replace-text.js +++ b/lib/rules/prefer-replace-text.js @@ -16,7 +16,8 @@ module.exports = { meta: { type: 'suggestion', docs: { - description: 'require using `replaceText()` instead of `replaceTextRange()`', + description: + 'require using `replaceText()` instead of `replaceTextRange()`', category: 'Rules', recommended: false, url: 'https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/prefer-replace-text.md', @@ -28,7 +29,7 @@ module.exports = { }, }, - create (context) { + create(context) { const sourceCode = context.getSourceCode(); let funcInfo = { upper: null, @@ -39,34 +40,44 @@ module.exports = { let contextIdentifiers; return { - Program (ast) { - contextIdentifiers = utils.getContextIdentifiers(sourceCode.scopeManager, ast); + Program(ast) { + contextIdentifiers = utils.getContextIdentifiers( + sourceCode.scopeManager, + ast + ); }, // Stacks this function's information. - onCodePathStart (codePath, node) { + onCodePathStart(codePath, node) { funcInfo = { upper: funcInfo, codePath, - shouldCheck: utils.isAutoFixerFunction(node, contextIdentifiers) || utils.isSuggestionFixerFunction(node, contextIdentifiers), + shouldCheck: + utils.isAutoFixerFunction(node, contextIdentifiers) || + utils.isSuggestionFixerFunction(node, contextIdentifiers), node, }; }, // Pops this function's information. - onCodePathEnd () { + onCodePathEnd() { funcInfo = funcInfo.upper; }, // Checks the replaceTextRange arguments. - 'CallExpression[arguments.length=2]' (node) { - if (funcInfo.shouldCheck && + 'CallExpression[arguments.length=2]'(node) { + if ( + funcInfo.shouldCheck && node.callee.type === 'MemberExpression' && - node.callee.property.name === 'replaceTextRange') { + node.callee.property.name === 'replaceTextRange' + ) { const arg = node.arguments[0]; - const isIdenticalNodeRange = arg.type === 'ArrayExpression' && - arg.elements[0].type === 'MemberExpression' && arg.elements[1].type === 'MemberExpression' && - sourceCode.getText(arg.elements[0].object) === sourceCode.getText(arg.elements[1].object); + const isIdenticalNodeRange = + arg.type === 'ArrayExpression' && + arg.elements[0].type === 'MemberExpression' && + arg.elements[1].type === 'MemberExpression' && + sourceCode.getText(arg.elements[0].object) === + sourceCode.getText(arg.elements[1].object); if (isIdenticalNodeRange) { context.report({ node, diff --git a/lib/rules/report-message-format.js b/lib/rules/report-message-format.js index d26df4d0..f98b78c2 100644 --- a/lib/rules/report-message-format.js +++ b/lib/rules/report-message-format.js @@ -23,15 +23,13 @@ module.exports = { url: 'https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/report-message-format.md', }, fixable: null, - schema: [ - { type: 'string' }, - ], + schema: [{ type: 'string' }], messages: { noMatch: "Report message does not match the pattern '{{pattern}}'.", }, }, - create (context) { + create(context) { const pattern = new RegExp(context.options[0] || ''); let contextIdentifiers; @@ -40,11 +38,15 @@ module.exports = { * @param {ASTNode} message The message AST node * @returns {void} */ - function processMessageNode (message) { + function processMessageNode(message) { const staticValue = getStaticValue(message, context.getScope()); if ( - (message.type === 'Literal' && typeof message.value === 'string' && !pattern.test(message.value)) || - (message.type === 'TemplateLiteral' && message.quasis.length === 1 && !pattern.test(message.quasis[0].value.cooked)) || + (message.type === 'Literal' && + typeof message.value === 'string' && + !pattern.test(message.value)) || + (message.type === 'TemplateLiteral' && + message.quasis.length === 1 && + !pattern.test(message.quasis[0].value.cooked)) || (staticValue && !pattern.test(staticValue.value)) ) { context.report({ @@ -60,29 +62,40 @@ module.exports = { // ---------------------------------------------------------------------- return { - Program (ast) { + Program(ast) { const sourceCode = context.getSourceCode(); - contextIdentifiers = utils.getContextIdentifiers(sourceCode.scopeManager, ast); + contextIdentifiers = utils.getContextIdentifiers( + sourceCode.scopeManager, + ast + ); const ruleInfo = utils.getRuleInfo(sourceCode); - const messagesObject = ruleInfo && + const messagesObject = + ruleInfo && ruleInfo.meta && ruleInfo.meta.type === 'ObjectExpression' && - ruleInfo.meta.properties.find(prop => prop.type === 'Property' && utils.getKeyName(prop) === 'messages'); + ruleInfo.meta.properties.find( + (prop) => + prop.type === 'Property' && utils.getKeyName(prop) === 'messages' + ); - if (!messagesObject || messagesObject.value.type !== 'ObjectExpression') { + if ( + !messagesObject || + messagesObject.value.type !== 'ObjectExpression' + ) { return; } messagesObject.value.properties - .filter(prop => prop.type === 'Property') - .map(prop => prop.value) + .filter((prop) => prop.type === 'Property') + .map((prop) => prop.value) .forEach(processMessageNode); }, - CallExpression (node) { + CallExpression(node) { if ( node.callee.type === 'MemberExpression' && contextIdentifiers.has(node.callee.object) && - node.callee.property.type === 'Identifier' && node.callee.property.name === 'report' + node.callee.property.type === 'Identifier' && + node.callee.property.name === 'report' ) { const reportInfo = utils.getReportInfo(node.arguments, context); const message = reportInfo && reportInfo.message; @@ -94,9 +107,14 @@ module.exports = { if (suggest && suggest.type === 'ArrayExpression') { suggest.elements - .flatMap(obj => obj.properties) - .filter(prop => prop.type === 'Property' && prop.key.type === 'Identifier' && prop.key.name === 'message') - .map(prop => prop.value) + .flatMap((obj) => obj.properties) + .filter( + (prop) => + prop.type === 'Property' && + prop.key.type === 'Identifier' && + prop.key.name === 'message' + ) + .map((prop) => prop.value) .forEach(processMessageNode); } } diff --git a/lib/rules/require-meta-docs-description.js b/lib/rules/require-meta-docs-description.js index f949d9f7..d3346eb5 100644 --- a/lib/rules/require-meta-docs-description.js +++ b/lib/rules/require-meta-docs-description.js @@ -14,7 +14,8 @@ module.exports = { meta: { type: 'suggestion', docs: { - description: 'require rules to implement a `meta.docs.description` property with the correct format', + description: + 'require rules to implement a `meta.docs.description` property with the correct format', category: 'Rules', recommended: false, url: 'https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/require-meta-docs-description.md', @@ -32,16 +33,17 @@ module.exports = { }, ], messages: { - extraWhitespace: '`meta.docs.description` must not have leading nor trailing whitespace.', + extraWhitespace: + '`meta.docs.description` must not have leading nor trailing whitespace.', mismatch: '`meta.docs.description` must match the regexp {{pattern}}.', missing: '`meta.docs.description` is required.', wrongType: '`meta.docs.description` must be a non-empty string.', }, }, - create (context) { + create(context) { return { - Program () { + Program() { const sourceCode = context.getSourceCode(); const info = utils.getRuleInfo(sourceCode); @@ -49,34 +51,54 @@ module.exports = { return; } - const pattern = context.options[0] && context.options[0].pattern ? new RegExp(context.options[0].pattern) : DEFAULT_PATTERN; + const pattern = + context.options[0] && context.options[0].pattern + ? new RegExp(context.options[0].pattern) + : DEFAULT_PATTERN; const metaNode = info.meta; const docsNode = metaNode && metaNode.properties && - metaNode.properties.find(p => p.type === 'Property' && utils.getKeyName(p) === 'docs'); + metaNode.properties.find( + (p) => p.type === 'Property' && utils.getKeyName(p) === 'docs' + ); const descriptionNode = docsNode && docsNode.value.properties && - docsNode.value.properties.find(p => p.type === 'Property' && utils.getKeyName(p) === 'description'); + docsNode.value.properties.find( + (p) => + p.type === 'Property' && utils.getKeyName(p) === 'description' + ); if (!descriptionNode) { - context.report({ node: docsNode || metaNode || info.create, messageId: 'missing' }); + context.report({ + node: docsNode || metaNode || info.create, + messageId: 'missing', + }); return; } - const staticValue = getStaticValue(descriptionNode.value, context.getScope()); + const staticValue = getStaticValue( + descriptionNode.value, + context.getScope() + ); if (!staticValue) { // Ignore non-static values since we can't determine what they look like. return; } if (typeof staticValue.value !== 'string' || staticValue.value === '') { - context.report({ node: descriptionNode.value, messageId: 'wrongType' }); + context.report({ + node: descriptionNode.value, + messageId: 'wrongType', + }); } else if (staticValue.value !== staticValue.value.trim()) { - context.report({ node: descriptionNode.value, messageId: 'extraWhitespace' }); + context.report({ + node: descriptionNode.value, + messageId: 'extraWhitespace', + }); } else if (!pattern.test(staticValue.value)) { context.report({ node: descriptionNode.value, diff --git a/lib/rules/require-meta-docs-url.js b/lib/rules/require-meta-docs-url.js index 19b290fb..6c0dee2b 100644 --- a/lib/rules/require-meta-docs-url.js +++ b/lib/rules/require-meta-docs-url.js @@ -27,13 +27,15 @@ module.exports = { url: 'https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/require-meta-docs-url.md', }, fixable: 'code', - schema: [{ - type: 'object', - properties: { - pattern: { type: 'string' }, + schema: [ + { + type: 'object', + properties: { + pattern: { type: 'string' }, + }, + additionalProperties: false, }, - additionalProperties: false, - }], + ], messages: { mismatch: '`meta.docs.url` property must be `{{expectedUrl}}`.', missing: '`meta.docs.url` property is missing.', @@ -46,32 +48,33 @@ module.exports = { * @param {RuleContext} context - The rule context. * @returns {Object} AST event handlers. */ - create (context) { + create(context) { const options = context.options[0] || {}; const sourceCode = context.getSourceCode(); const filename = context.getFilename(); - const ruleName = filename === '' ? undefined : path.basename(filename, path.extname(filename)); - const expectedUrl = !options.pattern || !ruleName - ? undefined - : options.pattern.replace(/{{\s*name\s*}}/g, ruleName); + const ruleName = + filename === '' + ? undefined + : path.basename(filename, path.extname(filename)); + const expectedUrl = + !options.pattern || !ruleName + ? undefined + : options.pattern.replace(/{{\s*name\s*}}/g, ruleName); /** * Check whether a given URL is the expected URL. * @param {string} url The URL to check. * @returns {boolean} `true` if the node is the expected URL. */ - function isExpectedUrl (url) { + function isExpectedUrl(url) { return Boolean( typeof url === 'string' && - ( - expectedUrl === undefined || - url === expectedUrl - ) + (expectedUrl === undefined || url === expectedUrl) ); } return { - Program () { + Program() { const info = util.getRuleInfo(sourceCode); if (info === null) { return; @@ -81,13 +84,19 @@ module.exports = { const docsPropNode = metaNode && metaNode.properties && - metaNode.properties.find(p => p.type === 'Property' && util.getKeyName(p) === 'docs'); + metaNode.properties.find( + (p) => p.type === 'Property' && util.getKeyName(p) === 'docs' + ); const urlPropNode = docsPropNode && docsPropNode.value.properties && - docsPropNode.value.properties.find(p => p.type === 'Property' && util.getKeyName(p) === 'url'); + docsPropNode.value.properties.find( + (p) => p.type === 'Property' && util.getKeyName(p) === 'url' + ); - const staticValue = urlPropNode ? getStaticValue(urlPropNode.value, context.getScope()) : undefined; + const staticValue = urlPropNode + ? getStaticValue(urlPropNode.value, context.getScope()) + : undefined; if (urlPropNode && !staticValue) { // Ignore non-static values since we can't determine what they look like. return; @@ -98,32 +107,58 @@ module.exports = { } context.report({ - node: (urlPropNode && urlPropNode.value) || (docsPropNode && docsPropNode.value) || metaNode || info.create, - - messageId: - !urlPropNode ? 'missing' : - // eslint-disable-next-line unicorn/no-nested-ternary - !expectedUrl ? 'wrongType' : - /* otherwise */ 'mismatch', + node: + (urlPropNode && urlPropNode.value) || + (docsPropNode && docsPropNode.value) || + metaNode || + info.create, + + messageId: !urlPropNode + ? 'missing' + : // eslint-disable-next-line unicorn/no-nested-ternary + !expectedUrl + ? 'wrongType' + : /* otherwise */ 'mismatch', data: { expectedUrl, }, - fix (fixer) { + fix(fixer) { if (!expectedUrl) { return null; } const urlString = JSON.stringify(expectedUrl); if (urlPropNode) { - if (urlPropNode.value.type === 'Literal' || (urlPropNode.value.type === 'Identifier' && urlPropNode.value.name === 'undefined')) { + if ( + urlPropNode.value.type === 'Literal' || + (urlPropNode.value.type === 'Identifier' && + urlPropNode.value.name === 'undefined') + ) { return fixer.replaceText(urlPropNode.value, urlString); } - } else if (docsPropNode && docsPropNode.value.type === 'ObjectExpression') { - return util.insertProperty(fixer, docsPropNode.value, `url: ${urlString}`, sourceCode); - } else if (!docsPropNode && metaNode && metaNode.type === 'ObjectExpression') { - return util.insertProperty(fixer, metaNode, `docs: {\nurl: ${urlString}\n}`, sourceCode); + } else if ( + docsPropNode && + docsPropNode.value.type === 'ObjectExpression' + ) { + return util.insertProperty( + fixer, + docsPropNode.value, + `url: ${urlString}`, + sourceCode + ); + } else if ( + !docsPropNode && + metaNode && + metaNode.type === 'ObjectExpression' + ) { + return util.insertProperty( + fixer, + metaNode, + `docs: {\nurl: ${urlString}\n}`, + sourceCode + ); } return null; diff --git a/lib/rules/require-meta-fixable.js b/lib/rules/require-meta-fixable.js index 510f6743..506e7619 100644 --- a/lib/rules/require-meta-fixable.js +++ b/lib/rules/require-meta-fixable.js @@ -36,13 +36,16 @@ module.exports = { ], messages: { invalid: '`meta.fixable` must be either `code`, `whitespace`, or `null`.', - missing: '`meta.fixable` must be either `code` or `whitespace` for fixable rules.', - noFixerButFixableValue: '`meta.fixable` is enabled but no fixer detected.', + missing: + '`meta.fixable` must be either `code` or `whitespace` for fixable rules.', + noFixerButFixableValue: + '`meta.fixable` is enabled but no fixer detected.', }, }, - create (context) { - const catchNoFixerButFixableProperty = context.options[0] && context.options[0].catchNoFixerButFixableProperty; + create(context) { + const catchNoFixerButFixableProperty = + context.options[0] && context.options[0].catchNoFixerButFixableProperty; const sourceCode = context.getSourceCode(); const ruleInfo = utils.getRuleInfo(sourceCode); @@ -58,53 +61,84 @@ module.exports = { // ---------------------------------------------------------------------- return { - Program (ast) { - contextIdentifiers = utils.getContextIdentifiers(sourceCode.scopeManager, ast); + Program(ast) { + contextIdentifiers = utils.getContextIdentifiers( + sourceCode.scopeManager, + ast + ); }, - CallExpression (node) { + CallExpression(node) { if ( node.callee.type === 'MemberExpression' && contextIdentifiers.has(node.callee.object) && node.callee.property.type === 'Identifier' && node.callee.property.name === 'report' && - (node.arguments.length > 4 || ( - node.arguments.length === 1 && - node.arguments[0].type === 'ObjectExpression' && - node.arguments[0].properties.some(prop => utils.getKeyName(prop) === 'fix') - )) + (node.arguments.length > 4 || + (node.arguments.length === 1 && + node.arguments[0].type === 'ObjectExpression' && + node.arguments[0].properties.some( + (prop) => utils.getKeyName(prop) === 'fix' + ))) ) { usesFixFunctions = true; } }, - 'Program:exit' () { - const metaFixableProp = ruleInfo && + 'Program:exit'() { + const metaFixableProp = + ruleInfo && ruleInfo.meta && ruleInfo.meta.type === 'ObjectExpression' && - ruleInfo.meta.properties.find(prop => utils.getKeyName(prop) === 'fixable'); + ruleInfo.meta.properties.find( + (prop) => utils.getKeyName(prop) === 'fixable' + ); if (metaFixableProp) { - const staticValue = getStaticValue(metaFixableProp.value, context.getScope()); + const staticValue = getStaticValue( + metaFixableProp.value, + context.getScope() + ); if (!staticValue) { // Ignore non-static values since we can't determine what they look like. return; } - if (!['code', 'whitespace', null, undefined].includes(staticValue.value)) { + if ( + !['code', 'whitespace', null, undefined].includes(staticValue.value) + ) { // `fixable` property has an invalid value. - context.report({ node: metaFixableProp.value, messageId: 'invalid' }); + context.report({ + node: metaFixableProp.value, + messageId: 'invalid', + }); return; } - if (usesFixFunctions && !['code', 'whitespace'].includes(staticValue.value)) { + if ( + usesFixFunctions && + !['code', 'whitespace'].includes(staticValue.value) + ) { // Rule is fixable but `fixable` property does not have a fixable value. - context.report({ node: metaFixableProp.value, messageId: 'missing' }); - } else if (catchNoFixerButFixableProperty && !usesFixFunctions && ['code', 'whitespace'].includes(staticValue.value)) { + context.report({ + node: metaFixableProp.value, + messageId: 'missing', + }); + } else if ( + catchNoFixerButFixableProperty && + !usesFixFunctions && + ['code', 'whitespace'].includes(staticValue.value) + ) { // Rule is NOT fixable but `fixable` property has a fixable value. - context.report({ node: metaFixableProp.value, messageId: 'noFixerButFixableValue' }); + context.report({ + node: metaFixableProp.value, + messageId: 'noFixerButFixableValue', + }); } } else if (!metaFixableProp && usesFixFunctions) { // Rule is fixable but is missing the `fixable` property. - context.report({ node: ruleInfo.meta || ruleInfo.create, messageId: 'missing' }); + context.report({ + node: ruleInfo.meta || ruleInfo.create, + messageId: 'missing', + }); } }, }; diff --git a/lib/rules/require-meta-has-suggestions.js b/lib/rules/require-meta-has-suggestions.js index 88aa03fa..ccaf93e0 100644 --- a/lib/rules/require-meta-has-suggestions.js +++ b/lib/rules/require-meta-has-suggestions.js @@ -12,7 +12,8 @@ module.exports = { meta: { type: 'problem', docs: { - description: 'require suggestable rules to implement a `meta.hasSuggestions` property', + description: + 'require suggestable rules to implement a `meta.hasSuggestions` property', category: 'Rules', recommended: true, url: 'https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/require-meta-has-suggestions.md', @@ -20,39 +21,51 @@ module.exports = { fixable: 'code', schema: [], messages: { - shouldBeSuggestable: '`meta.hasSuggestions` must be `true` for suggestable rules.', - shouldNotBeSuggestable: '`meta.hasSuggestions` cannot be `true` for non-suggestable rules.', + shouldBeSuggestable: + '`meta.hasSuggestions` must be `true` for suggestable rules.', + shouldNotBeSuggestable: + '`meta.hasSuggestions` cannot be `true` for non-suggestable rules.', }, }, - create (context) { + create(context) { const sourceCode = context.getSourceCode(); const ruleInfo = utils.getRuleInfo(sourceCode); let contextIdentifiers; let ruleReportsSuggestions; return { - Program (ast) { - contextIdentifiers = utils.getContextIdentifiers(sourceCode.scopeManager, ast); + Program(ast) { + contextIdentifiers = utils.getContextIdentifiers( + sourceCode.scopeManager, + ast + ); }, - CallExpression (node) { + CallExpression(node) { if ( node.callee.type === 'MemberExpression' && contextIdentifiers.has(node.callee.object) && node.callee.property.type === 'Identifier' && node.callee.property.name === 'report' && - (node.arguments.length > 4 || ( - node.arguments.length === 1 && - node.arguments[0].type === 'ObjectExpression' - )) + (node.arguments.length > 4 || + (node.arguments.length === 1 && + node.arguments[0].type === 'ObjectExpression')) ) { - const suggestProp = node.arguments[0].properties.find(prop => utils.getKeyName(prop) === 'suggest'); + const suggestProp = node.arguments[0].properties.find( + (prop) => utils.getKeyName(prop) === 'suggest' + ); if (suggestProp) { - const staticValue = getStaticValue(suggestProp.value, context.getScope()); + const staticValue = getStaticValue( + suggestProp.value, + context.getScope() + ); if ( !staticValue || - (Array.isArray(staticValue.value) && staticValue.value.length > 0) || - (Array.isArray(staticValue.value) && staticValue.value.length === 0 && suggestProp.value.type === 'Identifier') // Array variable can have suggestions pushed to it. + (Array.isArray(staticValue.value) && + staticValue.value.length > 0) || + (Array.isArray(staticValue.value) && + staticValue.value.length === 0 && + suggestProp.value.type === 'Identifier') // Array variable can have suggestions pushed to it. ) { // These are all considered reporting suggestions: // suggest: [{...}] @@ -63,10 +76,17 @@ module.exports = { } } }, - 'Program:exit' () { + 'Program:exit'() { const metaNode = ruleInfo && ruleInfo.meta; - const hasSuggestionsProperty = metaNode && metaNode.type === 'ObjectExpression' ? metaNode.properties.find(prop => utils.getKeyName(prop) === 'hasSuggestions') : undefined; - const hasSuggestionsStaticValue = hasSuggestionsProperty && getStaticValue(hasSuggestionsProperty.value, context.getScope()); + const hasSuggestionsProperty = + metaNode && metaNode.type === 'ObjectExpression' + ? metaNode.properties.find( + (prop) => utils.getKeyName(prop) === 'hasSuggestions' + ) + : undefined; + const hasSuggestionsStaticValue = + hasSuggestionsProperty && + getStaticValue(hasSuggestionsProperty.value, context.getScope()); if (ruleReportsSuggestions) { if (!hasSuggestionsProperty) { @@ -74,32 +94,56 @@ module.exports = { context.report({ node: metaNode || ruleInfo.create, messageId: 'shouldBeSuggestable', - fix (fixer) { + fix(fixer) { if (metaNode && metaNode.type === 'ObjectExpression') { if (metaNode.properties.length === 0) { // If object is empty, just replace entire object. - return fixer.replaceText(metaNode, '{ hasSuggestions: true }'); + return fixer.replaceText( + metaNode, + '{ hasSuggestions: true }' + ); } // Add new property to start of property list. - return fixer.insertTextBefore(metaNode.properties[0], 'hasSuggestions: true, '); + return fixer.insertTextBefore( + metaNode.properties[0], + 'hasSuggestions: true, ' + ); } }, }); - } else if (hasSuggestionsStaticValue && hasSuggestionsStaticValue.value !== true) { + } else if ( + hasSuggestionsStaticValue && + hasSuggestionsStaticValue.value !== true + ) { // Rule reports suggestions but does not have `meta.hasSuggestions` property enabled. context.report({ node: hasSuggestionsProperty.value, messageId: 'shouldBeSuggestable', - fix (fixer) { - if (hasSuggestionsProperty.value.type === 'Literal' || (hasSuggestionsProperty.value.type === 'Identifier' && hasSuggestionsProperty.value.name === 'undefined')) { - return fixer.replaceText(hasSuggestionsProperty.value, 'true'); + fix(fixer) { + if ( + hasSuggestionsProperty.value.type === 'Literal' || + (hasSuggestionsProperty.value.type === 'Identifier' && + hasSuggestionsProperty.value.name === 'undefined') + ) { + return fixer.replaceText( + hasSuggestionsProperty.value, + 'true' + ); } }, }); } - } else if (!ruleReportsSuggestions && hasSuggestionsProperty && hasSuggestionsStaticValue && hasSuggestionsStaticValue.value === true) { + } else if ( + !ruleReportsSuggestions && + hasSuggestionsProperty && + hasSuggestionsStaticValue && + hasSuggestionsStaticValue.value === true + ) { // Rule does not report suggestions but has the `meta.hasSuggestions` property enabled. - context.report({ node: hasSuggestionsProperty.value, messageId: 'shouldNotBeSuggestable' }); + context.report({ + node: hasSuggestionsProperty.value, + messageId: 'shouldNotBeSuggestable', + }); } }, }; diff --git a/lib/rules/require-meta-schema.js b/lib/rules/require-meta-schema.js index dd4d2aa7..4f218f3c 100644 --- a/lib/rules/require-meta-schema.js +++ b/lib/rules/require-meta-schema.js @@ -32,13 +32,15 @@ module.exports = { ], messages: { addEmptySchema: 'Add empty schema indicating the rule has no options.', - foundOptionsUsage: '`meta.schema` has no schema defined but rule has options.', + foundOptionsUsage: + '`meta.schema` has no schema defined but rule has options.', missing: '`meta.schema` is required (use [] if rule has no schema).', - wrongType: '`meta.schema` should be an array or object (use [] if rule has no schema).', + wrongType: + '`meta.schema` should be an array or object (use [] if rule has no schema).', }, }, - create (context) { + create(context) { const sourceCode = context.getSourceCode(); const { scopeManager } = sourceCode; const info = utils.getRuleInfo(sourceCode); @@ -51,19 +53,23 @@ module.exports = { let schemaNode; // Options - const requireSchemaPropertyWhenOptionless = !context.options[0] || context.options[0].requireSchemaPropertyWhenOptionless; + const requireSchemaPropertyWhenOptionless = + !context.options[0] || + context.options[0].requireSchemaPropertyWhenOptionless; let hasEmptySchema = false; let isUsingOptions = false; return { - Program (ast) { + Program(ast) { contextIdentifiers = utils.getContextIdentifiers(scopeManager, ast); schemaNode = metaNode && metaNode.properties && - metaNode.properties.find(p => p.type === 'Property' && utils.getKeyName(p) === 'schema'); + metaNode.properties.find( + (p) => p.type === 'Property' && utils.getKeyName(p) === 'schema' + ); if (!schemaNode) { return; @@ -104,24 +110,34 @@ module.exports = { } }, - 'Program:exit' () { + 'Program:exit'() { if (!schemaNode && requireSchemaPropertyWhenOptionless) { context.report({ node: metaNode || info.create, messageId: 'missing', - suggest: !isUsingOptions && metaNode && metaNode.type === 'ObjectExpression' ? [ - { - messageId: 'addEmptySchema', - fix (fixer) { - return utils.insertProperty(fixer, metaNode, 'schema: []', sourceCode); - }, - }, - ] : [], + suggest: + !isUsingOptions && + metaNode && + metaNode.type === 'ObjectExpression' + ? [ + { + messageId: 'addEmptySchema', + fix(fixer) { + return utils.insertProperty( + fixer, + metaNode, + 'schema: []', + sourceCode + ); + }, + }, + ] + : [], }); } }, - MemberExpression (node) { + MemberExpression(node) { // Check if `context.options` was used when no options were defined in `meta.schema`. if ( (hasEmptySchema || !schemaNode) && @@ -131,7 +147,10 @@ module.exports = { node.property.name === 'options' ) { isUsingOptions = true; - context.report({ node: schemaNode || metaNode || info.create, messageId: 'foundOptionsUsage' }); + context.report({ + node: schemaNode || metaNode || info.create, + messageId: 'foundOptionsUsage', + }); } }, }; diff --git a/lib/rules/require-meta-type.js b/lib/rules/require-meta-type.js index 85e30695..12082b3c 100644 --- a/lib/rules/require-meta-type.js +++ b/lib/rules/require-meta-type.js @@ -26,18 +26,20 @@ module.exports = { fixable: null, schema: [], messages: { - missing: '`meta.type` is required (must be either `problem`, `suggestion`, or `layout`).', - unexpected: '`meta.type` must be either `problem`, `suggestion`, or `layout`.', + missing: + '`meta.type` is required (must be either `problem`, `suggestion`, or `layout`).', + unexpected: + '`meta.type` must be either `problem`, `suggestion`, or `layout`.', }, }, - create (context) { + create(context) { // ---------------------------------------------------------------------- // Public // ---------------------------------------------------------------------- return { - Program () { + Program() { const sourceCode = context.getSourceCode(); const info = utils.getRuleInfo(sourceCode); @@ -49,10 +51,15 @@ module.exports = { const typeNode = metaNode && metaNode.properties && - metaNode.properties.find(p => p.type === 'Property' && utils.getKeyName(p) === 'type'); + metaNode.properties.find( + (p) => p.type === 'Property' && utils.getKeyName(p) === 'type' + ); if (!typeNode) { - context.report({ node: metaNode || info.create, messageId: 'missing' }); + context.report({ + node: metaNode || info.create, + messageId: 'missing', + }); return; } diff --git a/lib/rules/test-case-property-ordering.js b/lib/rules/test-case-property-ordering.js index 08901f84..76d6e2e7 100644 --- a/lib/rules/test-case-property-ordering.js +++ b/lib/rules/test-case-property-ordering.js @@ -16,22 +16,26 @@ module.exports = { meta: { type: 'suggestion', docs: { - description: 'require the properties of a test case to be placed in a consistent order', + description: + 'require the properties of a test case to be placed in a consistent order', category: 'Tests', recommended: false, url: 'https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/test-case-property-ordering.md', }, fixable: 'code', - schema: [{ - type: 'array', - elements: { type: 'string' }, - }], + schema: [ + { + type: 'array', + elements: { type: 'string' }, + }, + ], messages: { - inconsistentOrder: 'The properties of a test case should be placed in a consistent order: [{{order}}].', + inconsistentOrder: + 'The properties of a test case should be placed in a consistent order: [{{order}}].', }, }, - create (context) { + create(context) { // ---------------------------------------------------------------------- // Public // ---------------------------------------------------------------------- @@ -49,10 +53,10 @@ module.exports = { const sourceCode = context.getSourceCode(); return { - Program (ast) { - utils.getTestInfo(context, ast).forEach(testRun => { - [testRun.valid, testRun.invalid].forEach(tests => { - tests.forEach(test => { + Program(ast) { + utils.getTestInfo(context, ast).forEach((testRun) => { + [testRun.valid, testRun.invalid].forEach((tests) => { + tests.forEach((test) => { const properties = (test && test.properties) || []; const keyNames = properties.map(utils.getKeyName); @@ -61,18 +65,32 @@ module.exports = { // current < lastChecked to catch unordered; // and lastChecked === -1 to catch extra properties before. - if (current > -1 && (current < lastChecked || lastChecked === -1)) { - let orderMsg = order.filter(item => keyNames.includes(item)); - orderMsg = [...orderMsg, ...lastChecked === -1 ? keyNames.filter(item => !order.includes(item)) : []]; + if ( + current > -1 && + (current < lastChecked || lastChecked === -1) + ) { + let orderMsg = order.filter((item) => + keyNames.includes(item) + ); + orderMsg = [ + ...orderMsg, + ...(lastChecked === -1 + ? keyNames.filter((item) => !order.includes(item)) + : []), + ]; context.report({ node: properties[i], messageId: 'inconsistentOrder', data: { order: orderMsg.join(', ') }, - fix (fixer) { + fix(fixer) { return orderMsg.map((key, index) => { - const propertyToInsert = properties[keyNames.indexOf(key)]; - return fixer.replaceText(properties[index], sourceCode.getText(propertyToInsert)); + const propertyToInsert = + properties[keyNames.indexOf(key)]; + return fixer.replaceText( + properties[index], + sourceCode.getText(propertyToInsert) + ); }); }, }); diff --git a/lib/rules/test-case-shorthand-strings.js b/lib/rules/test-case-shorthand-strings.js index 18048f55..64507ba9 100644 --- a/lib/rules/test-case-shorthand-strings.js +++ b/lib/rules/test-case-shorthand-strings.js @@ -16,19 +16,23 @@ module.exports = { meta: { type: 'suggestion', docs: { - description: 'enforce consistent usage of shorthand strings for test cases with no options', + description: + 'enforce consistent usage of shorthand strings for test cases with no options', category: 'Tests', recommended: false, url: 'https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/test-case-shorthand-strings.md', }, fixable: 'code', - schema: [{ enum: ['as-needed', 'never', 'consistent', 'consistent-as-needed'] }], + schema: [ + { enum: ['as-needed', 'never', 'consistent', 'consistent-as-needed'] }, + ], messages: { - useShorthand: 'Use {{preferred}} for this test case instead of {{actual}}.', + useShorthand: + 'Use {{preferred}} for this test case instead of {{actual}}.', }, }, - create (context) { + create(context) { const shorthandOption = context.options[0] || 'as-needed'; const sourceCode = context.getSourceCode(); @@ -37,51 +41,70 @@ module.exports = { // ---------------------------------------------------------------------- /** - * Reports test cases as necessary - * @param {object[]} cases A list of test case nodes - * @returns {void} - */ - function reportTestCases (cases) { - const caseInfoList = cases.map(testCase => { - if (testCase.type === 'Literal' || testCase.type === 'TemplateLiteral') { - return { node: testCase, shorthand: true, needsLongform: false }; - } - if (testCase.type === 'ObjectExpression') { - return { - node: testCase, - shorthand: false, - needsLongform: !(testCase.properties.length === 1 && utils.getKeyName(testCase.properties[0]) === 'code'), - }; - } - return null; - }).filter(Boolean); + * Reports test cases as necessary + * @param {object[]} cases A list of test case nodes + * @returns {void} + */ + function reportTestCases(cases) { + const caseInfoList = cases + .map((testCase) => { + if ( + testCase.type === 'Literal' || + testCase.type === 'TemplateLiteral' + ) { + return { node: testCase, shorthand: true, needsLongform: false }; + } + if (testCase.type === 'ObjectExpression') { + return { + node: testCase, + shorthand: false, + needsLongform: !( + testCase.properties.length === 1 && + utils.getKeyName(testCase.properties[0]) === 'code' + ), + }; + } + return null; + }) + .filter(Boolean); - const isConsistent = new Set(caseInfoList.map(caseInfo => caseInfo.shorthand)).size <= 1; - const hasCaseNeedingLongform = caseInfoList.some(caseInfo => caseInfo.needsLongform); + const isConsistent = + new Set(caseInfoList.map((caseInfo) => caseInfo.shorthand)).size <= 1; + const hasCaseNeedingLongform = caseInfoList.some( + (caseInfo) => caseInfo.needsLongform + ); - caseInfoList.filter({ - 'as-needed': caseInfo => !caseInfo.shorthand && !caseInfo.needsLongform, - never: caseInfo => caseInfo.shorthand, - consistent: isConsistent ? () => false : caseInfo => caseInfo.shorthand, - 'consistent-as-needed': caseInfo => caseInfo.shorthand === hasCaseNeedingLongform, - }[shorthandOption]).forEach(badCaseInfo => { - context.report({ - node: badCaseInfo.node, - messageId: 'useShorthand', - data: { - preferred: badCaseInfo.shorthand ? 'an object' : 'a string', - actual: badCaseInfo.shorthand ? 'a string' : 'an object', - }, - fix (fixer) { - return fixer.replaceText( - badCaseInfo.node, - badCaseInfo.shorthand - ? `{code: ${sourceCode.getText(badCaseInfo.node)}}` - : sourceCode.getText(badCaseInfo.node.properties[0].value) - ); - }, + caseInfoList + .filter( + { + 'as-needed': (caseInfo) => + !caseInfo.shorthand && !caseInfo.needsLongform, + never: (caseInfo) => caseInfo.shorthand, + consistent: isConsistent + ? () => false + : (caseInfo) => caseInfo.shorthand, + 'consistent-as-needed': (caseInfo) => + caseInfo.shorthand === hasCaseNeedingLongform, + }[shorthandOption] + ) + .forEach((badCaseInfo) => { + context.report({ + node: badCaseInfo.node, + messageId: 'useShorthand', + data: { + preferred: badCaseInfo.shorthand ? 'an object' : 'a string', + actual: badCaseInfo.shorthand ? 'a string' : 'an object', + }, + fix(fixer) { + return fixer.replaceText( + badCaseInfo.node, + badCaseInfo.shorthand + ? `{code: ${sourceCode.getText(badCaseInfo.node)}}` + : sourceCode.getText(badCaseInfo.node.properties[0].value) + ); + }, + }); }); - }); } // ---------------------------------------------------------------------- @@ -89,8 +112,12 @@ module.exports = { // ---------------------------------------------------------------------- return { - Program (ast) { - utils.getTestInfo(context, ast).map(testRun => testRun.valid).filter(Boolean).forEach(reportTestCases); + Program(ast) { + utils + .getTestInfo(context, ast) + .map((testRun) => testRun.valid) + .filter(Boolean) + .forEach(reportTestCases); }, }; }, diff --git a/lib/utils.js b/lib/utils.js index 8aca9200..03646026 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -4,11 +4,11 @@ const { getStaticValue, findVariable } = require('eslint-utils'); const estraverse = require('estraverse'); /** -* Determines whether a node is a 'normal' (i.e. non-async, non-generator) function expression. -* @param {ASTNode} node The node in question -* @returns {boolean} `true` if the node is a normal function expression -*/ -function isNormalFunctionExpression (node) { + * Determines whether a node is a 'normal' (i.e. non-async, non-generator) function expression. + * @param {ASTNode} node The node in question + * @returns {boolean} `true` if the node is a normal function expression + */ +function isNormalFunctionExpression(node) { const functionTypes = [ 'FunctionExpression', 'ArrowFunctionExpression', @@ -18,16 +18,17 @@ function isNormalFunctionExpression (node) { } /** -* Determines whether a node is constructing a RuleTester instance -* @param {ASTNode} node The node in question -* @returns {boolean} `true` if the node is probably constructing a RuleTester instance -*/ -function isRuleTesterConstruction (node) { - return node.type === 'NewExpression' && ( - (node.callee.type === 'Identifier' && node.callee.name === 'RuleTester') || - (node.callee.type === 'MemberExpression' && - node.callee.property.type === 'Identifier' && - node.callee.property.name === 'RuleTester') + * Determines whether a node is constructing a RuleTester instance + * @param {ASTNode} node The node in question + * @returns {boolean} `true` if the node is probably constructing a RuleTester instance + */ +function isRuleTesterConstruction(node) { + return ( + node.type === 'NewExpression' && + ((node.callee.type === 'Identifier' && node.callee.name === 'RuleTester') || + (node.callee.type === 'MemberExpression' && + node.callee.property.type === 'Identifier' && + node.callee.property.name === 'RuleTester')) ); } @@ -39,13 +40,16 @@ const INTERESTING_RULE_KEYS = new Set(['create', 'meta']); * @param {Set} interestingKeys * @returns Object */ -function collectInterestingProperties (properties, interestingKeys) { +function collectInterestingProperties(properties, interestingKeys) { // eslint-disable-next-line unicorn/prefer-object-from-entries return properties.reduce((parsedProps, prop) => { const keyValue = module.exports.getKeyName(prop); if (interestingKeys.has(keyValue)) { // In TypeScript, unwrap any usage of `{} as const`. - parsedProps[keyValue] = prop.value.type === 'TSAsExpression' ? prop.value.expression : prop.value; + parsedProps[keyValue] = + prop.value.type === 'TSAsExpression' + ? prop.value.expression + : prop.value; } return parsedProps; }, {}); @@ -56,11 +60,15 @@ function collectInterestingProperties (properties, interestingKeys) { * @param {Node} node * @returns {boolean} */ -function hasObjectReturn (node) { +function hasObjectReturn(node) { let foundMatch = false; estraverse.traverse(node, { - enter (child) { - if (child.type === 'ReturnStatement' && child.argument && child.argument.type === 'ObjectExpression') { + enter(child) { + if ( + child.type === 'ReturnStatement' && + child.argument && + child.argument.type === 'ObjectExpression' + ) { foundMatch = true; } }, @@ -74,7 +82,7 @@ function hasObjectReturn (node) { * @param {*} node * @returns {boolean} */ -function isFunctionRule (node) { +function isFunctionRule(node) { return ( isNormalFunctionExpression(node) && // Is a function definition. node.params.length === 1 && // The function has a single `context` argument. @@ -87,113 +95,145 @@ function isFunctionRule (node) { * @param {Node} node * @returns {boolean} */ -function isTypeScriptRuleHelper (node) { +function isTypeScriptRuleHelper(node) { return ( node.type === 'CallExpression' && - node.arguments.length === 1 && - node.arguments[0].type === 'ObjectExpression' && - // Check various TypeScript rule helper formats. - ( + node.arguments.length === 1 && + node.arguments[0].type === 'ObjectExpression' && + // Check various TypeScript rule helper formats. // createESLintRule({ ... }) - node.callee.type === 'Identifier' || - // util.createRule({ ... }) - (node.callee.type === 'MemberExpression' && node.callee.object.type === 'Identifier' && node.callee.property.type === 'Identifier') || - // ESLintUtils.RuleCreator(docsUrl)({ ... }) - (node.callee.type === 'CallExpression' && node.callee.callee.type === 'MemberExpression' && node.callee.callee.object.type === 'Identifier' && node.callee.callee.property.type === 'Identifier') - ) + (node.callee.type === 'Identifier' || + // util.createRule({ ... }) + (node.callee.type === 'MemberExpression' && + node.callee.object.type === 'Identifier' && + node.callee.property.type === 'Identifier') || + // ESLintUtils.RuleCreator(docsUrl)({ ... }) + (node.callee.type === 'CallExpression' && + node.callee.callee.type === 'MemberExpression' && + node.callee.callee.object.type === 'Identifier' && + node.callee.callee.property.type === 'Identifier')) ); } /** * Helper for `getRuleInfo`. Handles ESM and TypeScript rules. */ -function getRuleExportsESM (ast, scopeManager) { - return ast.body - .filter(statement => statement.type === 'ExportDefaultDeclaration') - .map(statement => statement.declaration) - // eslint-disable-next-line unicorn/prefer-object-from-entries - .reduce((currentExports, node) => { - if (node.type === 'ObjectExpression') { - // Check `export default { create() {}, meta: {} }` - return collectInterestingProperties(node.properties, INTERESTING_RULE_KEYS); - } else if (isFunctionRule(node)) { - // Check `export default function(context) { return { ... }; }` - return { create: node, meta: null, isNewStyle: false }; - } else if (isTypeScriptRuleHelper(node)) { - // Check `export default someTypeScriptHelper({ create() {}, meta: {} }); - return collectInterestingProperties(node.arguments[0].properties, INTERESTING_RULE_KEYS); - } else if (node.type === 'Identifier') { - const possibleRule = findVariableValue(node, scopeManager); - if (possibleRule) { - if (possibleRule.type === 'ObjectExpression') { - // Check `const possibleRule = { ... }; export default possibleRule; - return collectInterestingProperties(possibleRule.properties, INTERESTING_RULE_KEYS); - } else if (isTypeScriptRuleHelper(possibleRule)) { - // Check `const possibleRule = someTypeScriptHelper({ ... }); export default possibleRule; - return collectInterestingProperties(possibleRule.arguments[0].properties, INTERESTING_RULE_KEYS); +function getRuleExportsESM(ast, scopeManager) { + return ( + ast.body + .filter((statement) => statement.type === 'ExportDefaultDeclaration') + .map((statement) => statement.declaration) + // eslint-disable-next-line unicorn/prefer-object-from-entries + .reduce((currentExports, node) => { + if (node.type === 'ObjectExpression') { + // Check `export default { create() {}, meta: {} }` + return collectInterestingProperties( + node.properties, + INTERESTING_RULE_KEYS + ); + } else if (isFunctionRule(node)) { + // Check `export default function(context) { return { ... }; }` + return { create: node, meta: null, isNewStyle: false }; + } else if (isTypeScriptRuleHelper(node)) { + // Check `export default someTypeScriptHelper({ create() {}, meta: {} }); + return collectInterestingProperties( + node.arguments[0].properties, + INTERESTING_RULE_KEYS + ); + } else if (node.type === 'Identifier') { + const possibleRule = findVariableValue(node, scopeManager); + if (possibleRule) { + if (possibleRule.type === 'ObjectExpression') { + // Check `const possibleRule = { ... }; export default possibleRule; + return collectInterestingProperties( + possibleRule.properties, + INTERESTING_RULE_KEYS + ); + } else if (isTypeScriptRuleHelper(possibleRule)) { + // Check `const possibleRule = someTypeScriptHelper({ ... }); export default possibleRule; + return collectInterestingProperties( + possibleRule.arguments[0].properties, + INTERESTING_RULE_KEYS + ); + } } } - } - return currentExports; - }, {}); + return currentExports; + }, {}) + ); } /** * Helper for `getRuleInfo`. Handles CJS rules. */ -function getRuleExportsCJS (ast, scopeManager) { +function getRuleExportsCJS(ast, scopeManager) { let exportsVarOverridden = false; let exportsIsFunction = false; - return ast.body - .filter(statement => statement.type === 'ExpressionStatement') - .map(statement => statement.expression) - .filter(expression => expression.type === 'AssignmentExpression') - .filter(expression => expression.left.type === 'MemberExpression') - // eslint-disable-next-line unicorn/prefer-object-from-entries - .reduce((currentExports, node) => { - if ( - node.left.object.type === 'Identifier' && node.left.object.name === 'module' && - node.left.property.type === 'Identifier' && node.left.property.name === 'exports' - ) { - exportsVarOverridden = true; - if (isFunctionRule(node.right)) { - // Check `module.exports = function (context) { return { ... }; }` - - exportsIsFunction = true; - return { create: node.right, meta: null, isNewStyle: false }; - } else if (node.right.type === 'ObjectExpression') { - // Check `module.exports = { create: function () {}, meta: {} }` - - return collectInterestingProperties(node.right.properties, INTERESTING_RULE_KEYS); - } else if (node.right.type === 'Identifier') { - const possibleRule = findVariableValue(node.right, scopeManager); - if (possibleRule && possibleRule.type === 'ObjectExpression') { - // Check `const possibleRule = { ... }; module.exports = possibleRule; - return collectInterestingProperties(possibleRule.properties, INTERESTING_RULE_KEYS); + return ( + ast.body + .filter((statement) => statement.type === 'ExpressionStatement') + .map((statement) => statement.expression) + .filter((expression) => expression.type === 'AssignmentExpression') + .filter((expression) => expression.left.type === 'MemberExpression') + // eslint-disable-next-line unicorn/prefer-object-from-entries + .reduce((currentExports, node) => { + if ( + node.left.object.type === 'Identifier' && + node.left.object.name === 'module' && + node.left.property.type === 'Identifier' && + node.left.property.name === 'exports' + ) { + exportsVarOverridden = true; + if (isFunctionRule(node.right)) { + // Check `module.exports = function (context) { return { ... }; }` + + exportsIsFunction = true; + return { create: node.right, meta: null, isNewStyle: false }; + } else if (node.right.type === 'ObjectExpression') { + // Check `module.exports = { create: function () {}, meta: {} }` + + return collectInterestingProperties( + node.right.properties, + INTERESTING_RULE_KEYS + ); + } else if (node.right.type === 'Identifier') { + const possibleRule = findVariableValue(node.right, scopeManager); + if (possibleRule && possibleRule.type === 'ObjectExpression') { + // Check `const possibleRule = { ... }; module.exports = possibleRule; + return collectInterestingProperties( + possibleRule.properties, + INTERESTING_RULE_KEYS + ); + } } + return {}; + } else if ( + !exportsIsFunction && + node.left.object.type === 'MemberExpression' && + node.left.object.object.type === 'Identifier' && + node.left.object.object.name === 'module' && + node.left.object.property.type === 'Identifier' && + node.left.object.property.name === 'exports' && + node.left.property.type === 'Identifier' && + INTERESTING_RULE_KEYS.has(node.left.property.name) + ) { + // Check `module.exports.create = () => {}` + + currentExports[node.left.property.name] = node.right; + } else if ( + !exportsVarOverridden && + node.left.object.type === 'Identifier' && + node.left.object.name === 'exports' && + node.left.property.type === 'Identifier' && + INTERESTING_RULE_KEYS.has(node.left.property.name) + ) { + // Check `exports.create = () => {}` + + currentExports[node.left.property.name] = node.right; } - return {}; - } else if ( - !exportsIsFunction && - node.left.object.type === 'MemberExpression' && - node.left.object.object.type === 'Identifier' && node.left.object.object.name === 'module' && - node.left.object.property.type === 'Identifier' && node.left.object.property.name === 'exports' && - node.left.property.type === 'Identifier' && INTERESTING_RULE_KEYS.has(node.left.property.name) - ) { - // Check `module.exports.create = () => {}` - - currentExports[node.left.property.name] = node.right; - } else if ( - !exportsVarOverridden && - node.left.object.type === 'Identifier' && node.left.object.name === 'exports' && - node.left.property.type === 'Identifier' && INTERESTING_RULE_KEYS.has(node.left.property.name) - ) { - // Check `exports.create = () => {}` - - currentExports[node.left.property.name] = node.right; - } - return currentExports; - }, {}); + return currentExports; + }, {}) + ); } /** @@ -202,8 +242,10 @@ function getRuleExportsCJS (ast, scopeManager) { * @param {String} keyName * @returns property value */ -function findObjectPropertyValueByKeyName (obj, keyName) { - const property = obj.properties.find(prop => prop.key.type === 'Identifier' && prop.key.name === keyName); +function findObjectPropertyValueByKeyName(obj, keyName) { + const property = obj.properties.find( + (prop) => prop.key.type === 'Identifier' && prop.key.name === keyName + ); return property ? property.value : undefined; } @@ -213,18 +255,16 @@ function findObjectPropertyValueByKeyName (obj, keyName) { * @param {ScopeManager} scopeManager * @returns the first value (or function) that the given variable is initialized to. */ -function findVariableValue (node, scopeManager) { +function findVariableValue(node, scopeManager) { const variable = findVariable( scopeManager.acquire(node) || scopeManager.globalScope, node ); - if ( - variable && - variable.defs && - variable.defs[0] && - variable.defs[0].node - ) { - if (variable.defs[0].node.type === 'VariableDeclarator' && variable.defs[0].node.init) { + if (variable && variable.defs && variable.defs[0] && variable.defs[0].node) { + if ( + variable.defs[0].node.type === 'VariableDeclarator' && + variable.defs[0].node.init + ) { // Given node `x`, get `123` from `const x = 123;`. return variable.defs[0].node.init; } else if (variable.defs[0].node.type === 'FunctionDeclaration') { @@ -243,10 +283,16 @@ module.exports = { is an object, and `false` if module.exports is just the `create` function. If no valid ESLint rule info can be extracted from the file, the return value will be `null`. */ - getRuleInfo ({ ast, scopeManager }) { - const exportNodes = ast.sourceType === 'module' ? getRuleExportsESM(ast, scopeManager) : getRuleExportsCJS(ast, scopeManager); - - const createExists = Object.prototype.hasOwnProperty.call(exportNodes, 'create'); + getRuleInfo({ ast, scopeManager }) { + const exportNodes = + ast.sourceType === 'module' + ? getRuleExportsESM(ast, scopeManager) + : getRuleExportsCJS(ast, scopeManager); + + const createExists = Object.prototype.hasOwnProperty.call( + exportNodes, + 'create' + ); if (!createExists) { return null; } @@ -270,33 +316,37 @@ module.exports = { }, /** - * Gets all the identifiers referring to the `context` variable in a rule source file. Note that this function will - * only work correctly after traversing the AST has started (e.g. in the first `Program` node). - * @param {RuleContext} scopeManager - * @param {ASTNode} ast The `Program` node for the file - * @returns {Set} A Set of all `Identifier` nodes that are references to the `context` value for the file - */ - getContextIdentifiers (scopeManager, ast) { + * Gets all the identifiers referring to the `context` variable in a rule source file. Note that this function will + * only work correctly after traversing the AST has started (e.g. in the first `Program` node). + * @param {RuleContext} scopeManager + * @param {ASTNode} ast The `Program` node for the file + * @returns {Set} A Set of all `Identifier` nodes that are references to the `context` value for the file + */ + getContextIdentifiers(scopeManager, ast) { const ruleInfo = module.exports.getRuleInfo({ ast, scopeManager }); - if (!ruleInfo || ruleInfo.create.params.length === 0 || ruleInfo.create.params[0].type !== 'Identifier') { + if ( + !ruleInfo || + ruleInfo.create.params.length === 0 || + ruleInfo.create.params[0].type !== 'Identifier' + ) { return new Set(); } return new Set( - scopeManager.getDeclaredVariables(ruleInfo.create) - .find(variable => variable.name === ruleInfo.create.params[0].name) - .references - .map(ref => ref.identifier) + scopeManager + .getDeclaredVariables(ruleInfo.create) + .find((variable) => variable.name === ruleInfo.create.params[0].name) + .references.map((ref) => ref.identifier) ); }, /** - * Gets the key name of a Property, if it can be determined statically. - * @param {ASTNode} node The `Property` node - * @returns {string|null} The key name, or `null` if the name cannot be determined statically. - */ - getKeyName (property) { + * Gets the key name of a Property, if it can be determined statically. + * @param {ASTNode} node The `Property` node + * @returns {string|null} The key name, or `null` if the name cannot be determined statically. + */ + getKeyName(property) { if (!property.key) { // likely a SpreadElement or another non-standard node return null; @@ -307,28 +357,37 @@ module.exports = { if (property.key.type === 'Literal') { return '' + property.key.value; } - if (property.key.type === 'TemplateLiteral' && property.key.quasis.length === 1) { + if ( + property.key.type === 'TemplateLiteral' && + property.key.quasis.length === 1 + ) { return property.key.quasis[0].value.cooked; } return null; }, /** - * Performs static analysis on an AST to try to find test cases - * @param {RuleContext} context The `context` variable for the source file itself - * @param {ASTNode} ast The `Program` node for the file. - * @returns {object} An object with `valid` and `invalid` keys containing a list of AST nodes corresponding to tests - */ - getTestInfo (context, ast) { + * Performs static analysis on an AST to try to find test cases + * @param {RuleContext} context The `context` variable for the source file itself + * @param {ASTNode} ast The `Program` node for the file. + * @returns {object} An object with `valid` and `invalid` keys containing a list of AST nodes corresponding to tests + */ + getTestInfo(context, ast) { const runCalls = []; const variableIdentifiers = new Set(); - ast.body.forEach(statement => { + ast.body.forEach((statement) => { if (statement.type === 'VariableDeclaration') { - statement.declarations.forEach(declarator => { - if (declarator.init && isRuleTesterConstruction(declarator.init) && declarator.id.type === 'Identifier') { - context.getDeclaredVariables(declarator).forEach(variable => { - variable.references.filter(ref => ref.isRead()).forEach(ref => variableIdentifiers.add(ref.identifier)); + statement.declarations.forEach((declarator) => { + if ( + declarator.init && + isRuleTesterConstruction(declarator.init) && + declarator.id.type === 'Identifier' + ) { + context.getDeclaredVariables(declarator).forEach((variable) => { + variable.references + .filter((ref) => ref.isRead()) + .forEach((ref) => variableIdentifiers.add(ref.identifier)); }); } }); @@ -338,10 +397,8 @@ module.exports = { statement.type === 'ExpressionStatement' && statement.expression.type === 'CallExpression' && statement.expression.callee.type === 'MemberExpression' && - ( - isRuleTesterConstruction(statement.expression.callee.object) || - variableIdentifiers.has(statement.expression.callee.object) - ) && + (isRuleTesterConstruction(statement.expression.callee.object) || + variableIdentifiers.has(statement.expression.callee.object)) && statement.expression.callee.property.type === 'Identifier' && statement.expression.callee.property.name === 'run' ) { @@ -350,25 +407,39 @@ module.exports = { }); return runCalls - .filter(call => call.arguments.length >= 3 && call.arguments[2].type === 'ObjectExpression') - .map(call => call.arguments[2]) - .map(run => { - const validProperty = run.properties.find(prop => module.exports.getKeyName(prop) === 'valid'); - const invalidProperty = run.properties.find(prop => module.exports.getKeyName(prop) === 'invalid'); + .filter( + (call) => + call.arguments.length >= 3 && + call.arguments[2].type === 'ObjectExpression' + ) + .map((call) => call.arguments[2]) + .map((run) => { + const validProperty = run.properties.find( + (prop) => module.exports.getKeyName(prop) === 'valid' + ); + const invalidProperty = run.properties.find( + (prop) => module.exports.getKeyName(prop) === 'invalid' + ); return { - valid: validProperty && validProperty.value.type === 'ArrayExpression' ? validProperty.value.elements.filter(Boolean) : [], - invalid: invalidProperty && invalidProperty.value.type === 'ArrayExpression' ? invalidProperty.value.elements.filter(Boolean) : [], + valid: + validProperty && validProperty.value.type === 'ArrayExpression' + ? validProperty.value.elements.filter(Boolean) + : [], + invalid: + invalidProperty && invalidProperty.value.type === 'ArrayExpression' + ? invalidProperty.value.elements.filter(Boolean) + : [], }; }); }, /** - * Gets information on a report, given the arguments passed to context.report(). - * @param {ASTNode[]} reportArgs The arguments passed to context.report() - * @param {Context} context - */ - getReportInfo (reportArgs, context) { + * Gets information on a report, given the arguments passed to context.report(). + * @param {ASTNode[]} reportArgs The arguments passed to context.report() + * @param {Context} context + */ + getReportInfo(reportArgs, context) { // If there is exactly one argument, the API expects an object. // Otherwise, if the second argument is a string, the arguments are interpreted as // ['node', 'message', 'data', 'fix']. @@ -395,17 +466,23 @@ module.exports = { let keys; - const secondArgStaticValue = getStaticValue(reportArgs[1], context.getScope()); + const secondArgStaticValue = getStaticValue( + reportArgs[1], + context.getScope() + ); if ( - (secondArgStaticValue && typeof secondArgStaticValue.value === 'string') || + (secondArgStaticValue && + typeof secondArgStaticValue.value === 'string') || reportArgs[1].type === 'TemplateLiteral' ) { keys = ['node', 'message', 'data', 'fix']; } else if ( reportArgs[1].type === 'ObjectExpression' || reportArgs[1].type === 'ArrayExpression' || - (reportArgs[1].type === 'Literal' && typeof reportArgs[1].value !== 'string') || - (secondArgStaticValue && ['object', 'number'].includes(typeof secondArgStaticValue.value)) + (reportArgs[1].type === 'Literal' && + typeof reportArgs[1].value !== 'string') || + (secondArgStaticValue && + ['object', 'number'].includes(typeof secondArgStaticValue.value)) ) { keys = ['node', 'loc', 'message', 'data', 'fix']; } else { @@ -413,9 +490,11 @@ module.exports = { return null; } - return Object.fromEntries(keys - .slice(0, reportArgs.length) - .map((key, index) => [key, reportArgs[index]])); + return Object.fromEntries( + keys + .slice(0, reportArgs.length) + .map((key, index) => [key, reportArgs[index]]) + ); }, /** @@ -424,22 +503,28 @@ module.exports = { * @param {ASTNode} ast The AST of the file. This must have `parent` properties. * @returns {Set} A set of all identifiers referring to the `SourceCode` object. */ - getSourceCodeIdentifiers (scopeManager, ast) { - return new Set([...module.exports.getContextIdentifiers(scopeManager, ast)] - .filter(identifier => identifier.parent && - identifier.parent.type === 'MemberExpression' && - identifier === identifier.parent.object && - identifier.parent.property.type === 'Identifier' && - identifier.parent.property.name === 'getSourceCode' && - identifier.parent.parent.type === 'CallExpression' && - identifier.parent === identifier.parent.parent.callee && - identifier.parent.parent.parent.type === 'VariableDeclarator' && - identifier.parent.parent === identifier.parent.parent.parent.init && - identifier.parent.parent.parent.id.type === 'Identifier' - ) - .flatMap(identifier => scopeManager.getDeclaredVariables(identifier.parent.parent.parent)) - .flatMap(variable => variable.references) - .map(ref => ref.identifier)); + getSourceCodeIdentifiers(scopeManager, ast) { + return new Set( + [...module.exports.getContextIdentifiers(scopeManager, ast)] + .filter( + (identifier) => + identifier.parent && + identifier.parent.type === 'MemberExpression' && + identifier === identifier.parent.object && + identifier.parent.property.type === 'Identifier' && + identifier.parent.property.name === 'getSourceCode' && + identifier.parent.parent.type === 'CallExpression' && + identifier.parent === identifier.parent.parent.callee && + identifier.parent.parent.parent.type === 'VariableDeclarator' && + identifier.parent.parent === identifier.parent.parent.parent.init && + identifier.parent.parent.parent.id.type === 'Identifier' + ) + .flatMap((identifier) => + scopeManager.getDeclaredVariables(identifier.parent.parent.parent) + ) + .flatMap((variable) => variable.references) + .map((ref) => ref.identifier) + ); }, /** @@ -449,7 +534,7 @@ module.exports = { * @param {string} propertyText The property code to insert. * @returns {void} */ - insertProperty (fixer, node, propertyText, sourceCode) { + insertProperty(fixer, node, propertyText, sourceCode) { if (node.properties.length === 0) { return fixer.replaceText(node, `{\n${propertyText}\n}`); } @@ -464,7 +549,7 @@ module.exports = { * @param {Object} reportInfo - Result of getReportInfo(). * @returns {messageId?: String, message?: String, data?: Object, fix?: Function}[] */ - collectReportViolationAndSuggestionData (reportInfo) { + collectReportViolationAndSuggestionData(reportInfo) { return [ // Violation message { @@ -475,18 +560,22 @@ module.exports = { }, // Suggestion messages ...((reportInfo.suggest && reportInfo.suggest.elements) || []) - .map(suggestObjNode => { + .map((suggestObjNode) => { if (suggestObjNode.type !== 'ObjectExpression') { // Ignore non-objects (like variables or function calls). return null; } return { - messageId: findObjectPropertyValueByKeyName(suggestObjNode, 'messageId'), + messageId: findObjectPropertyValueByKeyName( + suggestObjNode, + 'messageId' + ), message: findObjectPropertyValueByKeyName(suggestObjNode, 'desc'), // Note: suggestion message named `desc` data: findObjectPropertyValueByKeyName(suggestObjNode, 'data'), fix: findObjectPropertyValueByKeyName(suggestObjNode, 'fix'), }; - }).filter(item => item !== null), + }) + .filter((item) => item !== null), ]; }, @@ -496,14 +585,16 @@ module.exports = { * @param {Node[]} contextIdentifiers * @returns {boolean} */ - isAutoFixerFunction (node, contextIdentifiers) { + isAutoFixerFunction(node, contextIdentifiers) { const parent = node.parent; - return ['FunctionExpression', 'ArrowFunctionExpression'].includes(node.type) && + return ( + ['FunctionExpression', 'ArrowFunctionExpression'].includes(node.type) && parent.parent.type === 'ObjectExpression' && parent.parent.parent.type === 'CallExpression' && contextIdentifiers.has(parent.parent.parent.callee.object) && parent.parent.parent.callee.property.name === 'report' && - module.exports.getReportInfo(parent.parent.parent.arguments).fix === node; + module.exports.getReportInfo(parent.parent.parent.arguments).fix === node + ); }, /** @@ -512,9 +603,11 @@ module.exports = { * @param {Node[]} contextIdentifiers * @returns {boolean} */ - isSuggestionFixerFunction (node, contextIdentifiers) { + isSuggestionFixerFunction(node, contextIdentifiers) { const parent = node.parent; - return (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') && + return ( + (node.type === 'FunctionExpression' || + node.type === 'ArrowFunctionExpression') && parent.type === 'Property' && parent.key.type === 'Identifier' && parent.key.name === 'fix' && @@ -525,8 +618,14 @@ module.exports = { parent.parent.parent.parent.key.name === 'suggest' && parent.parent.parent.parent.parent.type === 'ObjectExpression' && parent.parent.parent.parent.parent.parent.type === 'CallExpression' && - contextIdentifiers.has(parent.parent.parent.parent.parent.parent.callee.object) && - parent.parent.parent.parent.parent.parent.callee.property.name === 'report' && - module.exports.getReportInfo(parent.parent.parent.parent.parent.parent.arguments).suggest === parent.parent.parent; + contextIdentifiers.has( + parent.parent.parent.parent.parent.parent.callee.object + ) && + parent.parent.parent.parent.parent.parent.callee.property.name === + 'report' && + module.exports.getReportInfo( + parent.parent.parent.parent.parent.parent.arguments + ).suggest === parent.parent.parent + ); }, }; diff --git a/package.json b/package.json index a031c919..752578f0 100644 --- a/package.json +++ b/package.json @@ -49,9 +49,11 @@ "dirty-chai": "^2.0.1", "eslint": "^8.0.0", "eslint-config-not-an-aardvark": "^2.1.0", + "eslint-config-prettier": "^8.3.0", "eslint-plugin-eslint-plugin": "file:./", "eslint-plugin-markdown": "^2.0.1", "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-unicorn": "^37.0.0", "eslint-scope": "^6.0.0", "espree": "^9.0.0", @@ -61,6 +63,7 @@ "mocha": "^9.1.2", "npm-run-all": "^4.1.5", "nyc": "^15.1.0", + "prettier": "^2.5.1", "release-it": "^14.9.0", "typescript": "^4.4.3" }, diff --git a/tests/build/generate-readme-table.js b/tests/build/generate-readme-table.js index 8bf36685..ea09cfe7 100644 --- a/tests/build/generate-readme-table.js +++ b/tests/build/generate-readme-table.js @@ -6,7 +6,10 @@ const assert = require('chai').assert; describe('table in README.md', () => { it('is up-to-date', () => { - const actualReadme = fs.readFileSync(path.resolve(__dirname, '..', '..', 'README.md'), 'utf8'); + const actualReadme = fs.readFileSync( + path.resolve(__dirname, '..', '..', 'README.md'), + 'utf8' + ); const expectedReadme = require('../../build/generate-readme-table'); assert( diff --git a/tests/lib/index.js b/tests/lib/index.js index e56c83db..c94af68d 100644 --- a/tests/lib/index.js +++ b/tests/lib/index.js @@ -7,7 +7,7 @@ const RULE_NAMES = Object.keys(plugin.rules); describe('exported plugin', () => { describe('has a meta.docs.url property on each rule', () => { - RULE_NAMES.forEach(ruleName => { + RULE_NAMES.forEach((ruleName) => { it(ruleName, () => { assert.match( plugin.rules[ruleName].meta.docs.url, diff --git a/tests/lib/rule-setup.js b/tests/lib/rule-setup.js index 938aee49..f0d2d2ee 100644 --- a/tests/lib/rule-setup.js +++ b/tests/lib/rule-setup.js @@ -6,21 +6,24 @@ const assert = require('chai').assert; const plugin = require('../..'); const RULE_NAMES = Object.keys(plugin.rules); -const RULE_NAMES_RECOMMENDED = new Set(Object.keys(plugin.configs.recommended.rules)); +const RULE_NAMES_RECOMMENDED = new Set( + Object.keys(plugin.configs.recommended.rules) +); const MESSAGES = { fixable: '⚒️ The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#--fix) can automatically fix some of the problems reported by this rule.', configRecommended: '✔️ The `"extends": "plugin:eslint-plugin/recommended"` property in a configuration file enables this rule.', - hasSuggestions: '💡 Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).', + hasSuggestions: + '💡 Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).', }; /** * @param {string} string - to operate on * @returns the string with a capitalized first letter */ -function capitalizeFirstLetter (string) { +function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); } @@ -29,13 +32,13 @@ function capitalizeFirstLetter (string) { * @param {Object|Array} jsonSchema - the JSON schema to check * @returns {String[]} list of named options */ -function getAllNamedOptions (jsonSchema) { +function getAllNamedOptions(jsonSchema) { if (!jsonSchema) { return []; } if (Array.isArray(jsonSchema)) { - return jsonSchema.flatMap(item => getAllNamedOptions(item)); + return jsonSchema.flatMap((item) => getAllNamedOptions(item)); } if (jsonSchema.items) { @@ -57,8 +60,8 @@ describe('rule setup is correct', () => { assert.deepStrictEqual( RULE_NAMES, files - .filter(file => !file.startsWith('.')) - .map(file => file.replace('.js', '')) + .filter((file) => !file.startsWith('.')) + .map((file) => file.replace('.js', '')) ); }); @@ -68,11 +71,21 @@ describe('rule setup is correct', () => { describe(ruleName, () => { it('has the right properties', () => { const ALLOWED_CATEGORIES = ['Rules', 'Tests']; - assert.ok(ALLOWED_CATEGORIES.includes(rule.meta.docs.category), 'has an allowed category'); + assert.ok( + ALLOWED_CATEGORIES.includes(rule.meta.docs.category), + 'has an allowed category' + ); }); it('should have the right contents', () => { - const filePath = path.join(__dirname, '..', '..', 'lib', 'rules', `${ruleName}.js`); + const filePath = path.join( + __dirname, + '..', + '..', + 'lib', + 'rules', + `${ruleName}.js` + ); const file = readFileSync(filePath, 'utf8'); assert.ok( @@ -91,8 +104,8 @@ describe('rule setup is correct', () => { assert.deepStrictEqual( RULE_NAMES, files - .filter(file => !file.startsWith('.')) - .map(file => file.replace('.js', '')) + .filter((file) => !file.startsWith('.')) + .map((file) => file.replace('.js', '')) ); }); @@ -103,8 +116,8 @@ describe('rule setup is correct', () => { assert.deepStrictEqual( RULE_NAMES, files - .filter(file => !file.startsWith('.')) - .map(file => file.replace('.md', '')) + .filter((file) => !file.startsWith('.')) + .map((file) => file.replace('.md', '')) ); }); @@ -125,15 +138,32 @@ describe('rule setup is correct', () => { describe(ruleName, () => { it('should have the right contents (title, notices, etc)', () => { // Title - assert.strictEqual(lines[0], `# ${capitalizeFirstLetter(rule.meta.docs.description)} (${ruleName})`, 'first line has rule description and name'); + assert.strictEqual( + lines[0], + `# ${capitalizeFirstLetter( + rule.meta.docs.description + )} (${ruleName})`, + 'first line has rule description and name' + ); assert.strictEqual(lines[1], '', 'second line is blank'); // Rule Details - assert.ok(fileContents.includes('## Rule Details'), 'includes "## Rule Details" header'); + assert.ok( + fileContents.includes('## Rule Details'), + 'includes "## Rule Details" header' + ); // Examples - assert.ok(fileContents.includes('Examples of **incorrect** code for this rule'), 'includes incorrect examples'); - assert.ok(fileContents.includes('Examples of **correct** code for this rule'), 'includes correct examples'); + assert.ok( + fileContents.includes( + 'Examples of **incorrect** code for this rule' + ), + 'includes incorrect examples' + ); + assert.ok( + fileContents.includes('Examples of **correct** code for this rule'), + 'includes correct examples' + ); // Decide which notices should be shown at the top of the doc. const expectedNotices = []; @@ -158,31 +188,50 @@ describe('rule setup is correct', () => { let currentLineNumber = 1; for (const expectedNotice of expectedNotices) { assert.strictEqual(lines[currentLineNumber], ''); - assert.strictEqual(lines[currentLineNumber + 1], MESSAGES[expectedNotice]); + assert.strictEqual( + lines[currentLineNumber + 1], + MESSAGES[expectedNotice] + ); currentLineNumber += 2; } // Ensure that unexpected notices are not present. for (const unexpectedNotice of unexpectedNotices) { - assert.ok(!fileContents.includes(MESSAGES[unexpectedNotice]), 'does not include notice: ' + MESSAGES[unexpectedNotice]); + assert.ok( + !fileContents.includes(MESSAGES[unexpectedNotice]), + 'does not include notice: ' + MESSAGES[unexpectedNotice] + ); } // Check if the rule has configuration options. if ( (Array.isArray(rule.meta.schema) && rule.meta.schema.length > 0) || - (typeof rule.meta.schema === 'object' && Object.keys(rule.meta.schema).length > 0) + (typeof rule.meta.schema === 'object' && + Object.keys(rule.meta.schema).length > 0) ) { // Should have a configuration section header: - assert.ok(fileContents.includes('## Options'), 'Should have an "## Options" section'); + assert.ok( + fileContents.includes('## Options'), + 'Should have an "## Options" section' + ); // Ensure all configuration options are mentioned. for (const namedOption of getAllNamedOptions(rule.meta.schema)) { - assert.ok(fileContents.includes(namedOption), 'Should mention the `' + namedOption + '` option'); + assert.ok( + fileContents.includes(namedOption), + 'Should mention the `' + namedOption + '` option' + ); } } else { // Should NOT have any options/config section headers: - assert.notOk(fileContents.includes('# Options'), 'Should not have an "Options" section'); - assert.notOk(fileContents.includes('# Config'), 'Should not have a "Config" section'); + assert.notOk( + fileContents.includes('# Options'), + 'Should not have an "Options" section' + ); + assert.notOk( + fileContents.includes('# Config'), + 'Should not have a "Config" section' + ); } }); }); diff --git a/tests/lib/rules/fixer-return.js b/tests/lib/rules/fixer-return.js index 00f04168..76f833b2 100644 --- a/tests/lib/rules/fixer-return.js +++ b/tests/lib/rules/fixer-return.js @@ -286,7 +286,14 @@ ruleTester.run('fixer-return', rule, { } }; `, - errors: [{ messageId: 'missingFix', type: 'FunctionExpression', line: 5, column: 24 }], + errors: [ + { + messageId: 'missingFix', + type: 'FunctionExpression', + line: 5, + column: 24, + }, + ], }, { // Fix but missing return @@ -300,7 +307,14 @@ ruleTester.run('fixer-return', rule, { } module.exports = { create }; `, - errors: [{ messageId: 'missingFix', type: 'FunctionExpression', line: 4, column: 20 }], + errors: [ + { + messageId: 'missingFix', + type: 'FunctionExpression', + line: 4, + column: 20, + }, + ], }, { // Fix but missing return (suggestion) @@ -319,7 +333,14 @@ ruleTester.run('fixer-return', rule, { } }; `, - errors: [{ messageId: 'missingFix', type: 'FunctionExpression', line: 7, column: 36 }], + errors: [ + { + messageId: 'missingFix', + type: 'FunctionExpression', + line: 7, + column: 36, + }, + ], }, { // Fix but missing return (arrow function, report on arrow) @@ -334,7 +355,16 @@ ruleTester.run('fixer-return', rule, { } }; `, - errors: [{ messageId: 'missingFix', type: 'ArrowFunctionExpression', line: 5, endLine: 5, column: 34, endColumn: 36 }], + errors: [ + { + messageId: 'missingFix', + type: 'ArrowFunctionExpression', + line: 5, + endLine: 5, + column: 34, + endColumn: 36, + }, + ], }, { // Fix but missing return (arrow function, report on arrow, suggestion) @@ -353,7 +383,16 @@ ruleTester.run('fixer-return', rule, { } }; `, - errors: [{ messageId: 'missingFix', type: 'ArrowFunctionExpression', line: 7, endLine: 7, column: 46, endColumn: 48 }], + errors: [ + { + messageId: 'missingFix', + type: 'ArrowFunctionExpression', + line: 7, + endLine: 7, + column: 46, + endColumn: 48, + }, + ], }, { // With no autofix (arrow function, explicit return, report on arrow) @@ -368,7 +407,16 @@ ruleTester.run('fixer-return', rule, { } }; `, - errors: [{ messageId: 'missingFix', type: 'ArrowFunctionExpression', line: 5, endLine: 5, column: 34, endColumn: 36 }], + errors: [ + { + messageId: 'missingFix', + type: 'ArrowFunctionExpression', + line: 5, + endLine: 5, + column: 34, + endColumn: 36, + }, + ], }, { // With no autofix (arrow function, implied return, report on arrow) @@ -381,7 +429,16 @@ ruleTester.run('fixer-return', rule, { } }; `, - errors: [{ messageId: 'missingFix', type: 'ArrowFunctionExpression', line: 5, endLine: 5, column: 32, endColumn: 34 }], + errors: [ + { + messageId: 'missingFix', + type: 'ArrowFunctionExpression', + line: 5, + endLine: 5, + column: 32, + endColumn: 34, + }, + ], }, { // Fix but missing yield (generator) @@ -396,7 +453,14 @@ ruleTester.run('fixer-return', rule, { } }; `, - errors: [{ messageId: 'missingFix', type: 'FunctionExpression', line: 5, column: 25 }], + errors: [ + { + messageId: 'missingFix', + type: 'FunctionExpression', + line: 5, + column: 25, + }, + ], }, { // With no autofix (only yield undefined) @@ -411,7 +475,14 @@ ruleTester.run('fixer-return', rule, { } }; `, - errors: [{ messageId: 'missingFix', type: 'FunctionExpression', line: 5, column: 25 }], + errors: [ + { + messageId: 'missingFix', + type: 'FunctionExpression', + line: 5, + column: 25, + }, + ], }, { // With no autofix (only return null) @@ -426,7 +497,14 @@ ruleTester.run('fixer-return', rule, { } }; `, - errors: [{ messageId: 'missingFix', type: 'FunctionExpression', line: 5, column: 26 }], + errors: [ + { + messageId: 'missingFix', + type: 'FunctionExpression', + line: 5, + column: 26, + }, + ], }, { // With no autofix (only return undefined) @@ -441,7 +519,14 @@ ruleTester.run('fixer-return', rule, { } }; `, - errors: [{ messageId: 'missingFix', type: 'FunctionExpression', line: 5, column: 26 }], + errors: [ + { + messageId: 'missingFix', + type: 'FunctionExpression', + line: 5, + column: 26, + }, + ], }, { // With no autofix (only return undefined, but in variable) @@ -457,7 +542,14 @@ ruleTester.run('fixer-return', rule, { } }; `, - errors: [{ messageId: 'missingFix', type: 'FunctionExpression', line: 5, column: 26 }], + errors: [ + { + messageId: 'missingFix', + type: 'FunctionExpression', + line: 5, + column: 26, + }, + ], }, { // With no autofix (only return implicit undefined) @@ -472,7 +564,14 @@ ruleTester.run('fixer-return', rule, { } }; `, - errors: [{ messageId: 'missingFix', type: 'FunctionExpression', line: 5, column: 26 }], + errors: [ + { + messageId: 'missingFix', + type: 'FunctionExpression', + line: 5, + column: 26, + }, + ], }, { // With no autofix (only return empty array) @@ -487,7 +586,14 @@ ruleTester.run('fixer-return', rule, { } }; `, - errors: [{ messageId: 'missingFix', type: 'FunctionExpression', line: 5, column: 26 }], + errors: [ + { + messageId: 'missingFix', + type: 'FunctionExpression', + line: 5, + column: 26, + }, + ], }, { // With no autofix (no return, empty function) @@ -501,7 +607,14 @@ ruleTester.run('fixer-return', rule, { } }; `, - errors: [{ messageId: 'missingFix', type: 'FunctionExpression', line: 5, column: 26 }], + errors: [ + { + messageId: 'missingFix', + type: 'FunctionExpression', + line: 5, + column: 26, + }, + ], }, ], }); diff --git a/tests/lib/rules/meta-property-ordering.js b/tests/lib/rules/meta-property-ordering.js index 7e851ddc..ac3510af 100644 --- a/tests/lib/rules/meta-property-ordering.js +++ b/tests/lib/rules/meta-property-ordering.js @@ -95,7 +95,12 @@ ruleTester.run('test-case-property-ordering', rule, { }, create() {}, };`, - errors: [{ messageId: 'inconsistentOrder', data: { order: ['type', 'docs', 'fixable'].join(', ') } }], + errors: [ + { + messageId: 'inconsistentOrder', + data: { order: ['type', 'docs', 'fixable'].join(', ') }, + }, + ], }, { // ESM @@ -119,7 +124,12 @@ ruleTester.run('test-case-property-ordering', rule, { create() {}, };`, parserOptions: { sourceType: 'module' }, - errors: [{ messageId: 'inconsistentOrder', data: { order: ['type', 'docs', 'fixable'].join(', ') } }], + errors: [ + { + messageId: 'inconsistentOrder', + data: { order: ['type', 'docs', 'fixable'].join(', ') }, + }, + ], }, { code: ` @@ -134,8 +144,14 @@ ruleTester.run('test-case-property-ordering', rule, { create() {}, };`, errors: [ - { messageId: 'inconsistentOrder', data: { order: ['type', 'docs', 'fixable', 'schema'].join(', ') } }, - { messageId: 'inconsistentOrder', data: { order: ['type', 'docs', 'fixable', 'schema'].join(', ') } }, + { + messageId: 'inconsistentOrder', + data: { order: ['type', 'docs', 'fixable', 'schema'].join(', ') }, + }, + { + messageId: 'inconsistentOrder', + data: { order: ['type', 'docs', 'fixable', 'schema'].join(', ') }, + }, ], }, @@ -153,8 +169,14 @@ ruleTester.run('test-case-property-ordering', rule, { };`, options: [['type', 'docs', 'fixable']], errors: [ - { messageId: 'inconsistentOrder', data: { order: ['type', 'docs', 'fixable'].join(', ') } }, - { messageId: 'inconsistentOrder', data: { order: ['type', 'docs', 'fixable'].join(', ') } }, + { + messageId: 'inconsistentOrder', + data: { order: ['type', 'docs', 'fixable'].join(', ') }, + }, + { + messageId: 'inconsistentOrder', + data: { order: ['type', 'docs', 'fixable'].join(', ') }, + }, ], }, ], diff --git a/tests/lib/rules/no-deprecated-context-methods.js b/tests/lib/rules/no-deprecated-context-methods.js index 84e3ba9d..0e89963f 100644 --- a/tests/lib/rules/no-deprecated-context-methods.js +++ b/tests/lib/rules/no-deprecated-context-methods.js @@ -18,7 +18,6 @@ const RuleTester = require('eslint').RuleTester; const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } }); ruleTester.run('no-deprecated-context-methods', rule, { - valid: [ ` module.exports = { @@ -62,7 +61,8 @@ ruleTester.run('no-deprecated-context-methods', rule, { `, errors: [ { - message: 'Use `context.getSourceCode().getText` instead of `context.getSource`.', + message: + 'Use `context.getSourceCode().getText` instead of `context.getSource`.', type: 'MemberExpression', }, ], @@ -80,7 +80,8 @@ ruleTester.run('no-deprecated-context-methods', rule, { `, errors: [ { - message: 'Use `myRuleContext.getSourceCode().getFirstToken` instead of `myRuleContext.getFirstToken`.', + message: + 'Use `myRuleContext.getSourceCode().getFirstToken` instead of `myRuleContext.getFirstToken`.', type: 'MemberExpression', }, ], @@ -95,7 +96,13 @@ ruleTester.run('no-deprecated-context-methods', rule, { const create = function(context) { return { Program(ast) { context.getSourceCode().getText(ast); } } }; module.exports = { create }; `, - errors: [{ message: 'Use `context.getSourceCode().getText` instead of `context.getSource`.', type: 'MemberExpression' }], + errors: [ + { + message: + 'Use `context.getSourceCode().getText` instead of `context.getSource`.', + type: 'MemberExpression', + }, + ], }, ], }); diff --git a/tests/lib/rules/no-deprecated-report-api.js b/tests/lib/rules/no-deprecated-report-api.js index 0e65b591..58319b89 100644 --- a/tests/lib/rules/no-deprecated-report-api.js +++ b/tests/lib/rules/no-deprecated-report-api.js @@ -18,7 +18,6 @@ const RuleTester = require('eslint').RuleTester; const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } }); ruleTester.run('no-deprecated-report-api', rule, { - valid: [ ` module.exports = { diff --git a/tests/lib/rules/no-missing-placeholders.js b/tests/lib/rules/no-missing-placeholders.js index f41e1051..78d9a645 100644 --- a/tests/lib/rules/no-missing-placeholders.js +++ b/tests/lib/rules/no-missing-placeholders.js @@ -13,11 +13,11 @@ const rule = require('../../../lib/rules/no-missing-placeholders'); const RuleTester = require('eslint').RuleTester; /** -* Create an error for the given key -* @param {string} missingKey The placeholder that is missing -* @returns {object} An expected error -*/ -function error (missingKey, type = 'Literal') { + * Create an error for the given key + * @param {string} missingKey The placeholder that is missing + * @returns {object} An expected error + */ +function error(missingKey, type = 'Literal') { return { type, message: `The placeholder {{${missingKey}}} does not exist.` }; } @@ -27,7 +27,6 @@ function error (missingKey, type = 'Literal') { const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } }); ruleTester.run('no-missing-placeholders', rule, { - valid: [ ` module.exports = { diff --git a/tests/lib/rules/no-only-tests.js b/tests/lib/rules/no-only-tests.js index 8fb6e51c..007491fd 100644 --- a/tests/lib/rules/no-only-tests.js +++ b/tests/lib/rules/no-only-tests.js @@ -245,7 +245,17 @@ ruleTester.run('no-only-tests', rule, { }); `, output: null, - errors: [{ messageId: 'foundOnly', type: 'MemberExpression', line: 6, endLine: 6, column: 13, endColumn: 28, suggestions: [] }], + errors: [ + { + messageId: 'foundOnly', + type: 'MemberExpression', + line: 6, + endLine: 6, + column: 13, + endColumn: 28, + suggestions: [], + }, + ], }, ], }); diff --git a/tests/lib/rules/no-unused-placeholders.js b/tests/lib/rules/no-unused-placeholders.js index 71ff539f..cabdbc2c 100644 --- a/tests/lib/rules/no-unused-placeholders.js +++ b/tests/lib/rules/no-unused-placeholders.js @@ -13,11 +13,11 @@ const rule = require('../../../lib/rules/no-unused-placeholders'); const RuleTester = require('eslint').RuleTester; /** -* Create an error for the given key -* @param {string} unusedKey The placeholder that is unused -* @returns {object} An expected error -*/ -function error (unusedKey, type = 'Literal') { + * Create an error for the given key + * @param {string} unusedKey The placeholder that is unused + * @returns {object} An expected error + */ +function error(unusedKey, type = 'Literal') { return { type, message: `The placeholder {{${unusedKey}}} is unused.` }; } @@ -27,7 +27,6 @@ function error (unusedKey, type = 'Literal') { const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } }); ruleTester.run('no-unused-placeholders', rule, { - valid: [ ` module.exports = { diff --git a/tests/lib/rules/no-useless-token-range.js b/tests/lib/rules/no-useless-token-range.js index 8dc5cf39..5b756606 100644 --- a/tests/lib/rules/no-useless-token-range.js +++ b/tests/lib/rules/no-useless-token-range.js @@ -17,7 +17,7 @@ const RuleTester = require('eslint').RuleTester; * @param {string} code source text given a `sourceCode` variable * @returns {string} rule code containing that source text */ -function wrapRule (code) { +function wrapRule(code) { return ` module.exports = { create(context) { @@ -36,7 +36,9 @@ const INVALID_CASES = [ { code: 'sourceCode.getFirstToken(foo).range[0]', output: 'foo.range[0]', - errors: [{ message: "Use 'foo.range[0]' instead.", type: 'CallExpression' }], + errors: [ + { message: "Use 'foo.range[0]' instead.", type: 'CallExpression' }, + ], }, { code: 'sourceCode.getFirstToken(foo).start', @@ -46,7 +48,9 @@ const INVALID_CASES = [ { code: 'sourceCode.getLastToken(foo).range[1]', output: 'foo.range[1]', - errors: [{ message: "Use 'foo.range[1]' instead.", type: 'CallExpression' }], + errors: [ + { message: "Use 'foo.range[1]' instead.", type: 'CallExpression' }, + ], }, { code: 'sourceCode.getLastToken(foo).end', @@ -56,18 +60,26 @@ const INVALID_CASES = [ { code: 'sourceCode.getFirstToken(foo, { includeComments: true }).range[0]', output: 'foo.range[0]', - errors: [{ message: "Use 'foo.range[0]' instead.", type: 'CallExpression' }], + errors: [ + { message: "Use 'foo.range[0]' instead.", type: 'CallExpression' }, + ], }, { code: 'sourceCode.getLastToken(foo, { includeComments: true }).range[1]', output: 'foo.range[1]', - errors: [{ message: "Use 'foo.range[1]' instead.", type: 'CallExpression' }], + errors: [ + { message: "Use 'foo.range[1]' instead.", type: 'CallExpression' }, + ], }, -].map(invalidCase => Object.assign(invalidCase, { code: wrapRule(invalidCase.code), output: wrapRule(invalidCase.output) })); +].map((invalidCase) => + Object.assign(invalidCase, { + code: wrapRule(invalidCase.code), + output: wrapRule(invalidCase.output), + }) +); const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } }); ruleTester.run('no-useless-token-range', rule, { - valid: [ 'sourceCode.getLastToken(foo).range[0]', 'sourceCode.getFirstToken(foo).range[1]', @@ -99,7 +111,9 @@ ruleTester.run('no-useless-token-range', rule, { } module.exports = { create }; `, - errors: [{ message: "Use 'foo.range[0]' instead.", type: 'CallExpression' }], + errors: [ + { message: "Use 'foo.range[0]' instead.", type: 'CallExpression' }, + ], }, ], }); diff --git a/tests/lib/rules/report-message-format.js b/tests/lib/rules/report-message-format.js index 4082a631..1fed0869 100644 --- a/tests/lib/rules/report-message-format.js +++ b/tests/lib/rules/report-message-format.js @@ -12,14 +12,12 @@ const rule = require('../../../lib/rules/report-message-format'); const RuleTester = require('eslint').RuleTester; - // ------------------------------------------------------------------------------ // Tests // ------------------------------------------------------------------------------ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } }); ruleTester.run('report-message-format', rule, { - valid: [ // with no configuration, everything is allowed 'module.exports = context => { context.report(node, "foo"); return {}; }', @@ -306,9 +304,16 @@ ruleTester.run('report-message-format', rule, { `, options: ['foo'], }, - ].map(invalidCase => { - return Object.assign({ - errors: [{ message: `Report message does not match the pattern '${invalidCase.options[0]}'.` }], - }, invalidCase); + ].map((invalidCase) => { + return Object.assign( + { + errors: [ + { + message: `Report message does not match the pattern '${invalidCase.options[0]}'.`, + }, + ], + }, + invalidCase + ); }), }); diff --git a/tests/lib/rules/require-meta-docs-description.js b/tests/lib/rules/require-meta-docs-description.js index 5627051c..dc9849c4 100644 --- a/tests/lib/rules/require-meta-docs-description.js +++ b/tests/lib/rules/require-meta-docs-description.js @@ -82,8 +82,7 @@ ruleTester.run('require-meta-docs-description', rule, { }; `, { - code: - ` + code: ` module.exports = { meta: { docs: { description: 'myPrefix foo bar' } }, create(context) {} @@ -92,8 +91,7 @@ ruleTester.run('require-meta-docs-description', rule, { options: [{ pattern: '^myPrefix' }], }, { - code: - ` + code: ` module.exports = { meta: { docs: { description: 'random message' } }, create(context) {} @@ -242,7 +240,13 @@ ruleTester.run('require-meta-docs-description', rule, { }; `, output: null, - errors: [{ message: '`meta.docs.description` must match the regexp /^(enforce|require|disallow)/.', type: 'Literal' }], + errors: [ + { + message: + '`meta.docs.description` must match the regexp /^(enforce|require|disallow)/.', + type: 'Literal', + }, + ], }, { code: ` @@ -252,7 +256,13 @@ ruleTester.run('require-meta-docs-description', rule, { }; `, output: null, - errors: [{ message: '`meta.docs.description` must match the regexp /^(enforce|require|disallow)/.', type: 'BinaryExpression' }], + errors: [ + { + message: + '`meta.docs.description` must match the regexp /^(enforce|require|disallow)/.', + type: 'BinaryExpression', + }, + ], }, { code: ` @@ -263,7 +273,12 @@ ruleTester.run('require-meta-docs-description', rule, { `, output: null, options: [{ pattern: '^myPrefix' }], - errors: [{ message: '`meta.docs.description` must match the regexp /^myPrefix/.', type: 'Literal' }], + errors: [ + { + message: '`meta.docs.description` must match the regexp /^myPrefix/.', + type: 'Literal', + }, + ], }, ], }); diff --git a/tests/lib/rules/require-meta-docs-url.js b/tests/lib/rules/require-meta-docs-url.js index 6afb1890..49e54f1f 100644 --- a/tests/lib/rules/require-meta-docs-url.js +++ b/tests/lib/rules/require-meta-docs-url.js @@ -50,9 +50,11 @@ tester.run('require-meta-docs-url', rule, { create() {} } `, - options: [{ - pattern: 'path/to/{{name}}.md', - }], + options: [ + { + pattern: 'path/to/{{name}}.md', + }, + ], }, { filename: 'test-rule', @@ -62,9 +64,11 @@ tester.run('require-meta-docs-url', rule, { create() {} } `, - options: [{ - pattern: 'path/to/{{name}}.md', - }], + options: [ + { + pattern: 'path/to/{{name}}.md', + }, + ], }, { // CJS file extension @@ -86,9 +90,11 @@ tester.run('require-meta-docs-url', rule, { create() {} } `, - options: [{ - pattern: 'path/to/{{name}}.md', - }], + options: [ + { + pattern: 'path/to/{{name}}.md', + }, + ], parserOptions: { sourceType: 'module' }, }, { @@ -113,9 +119,11 @@ tester.run('require-meta-docs-url', rule, { create() {} } `, - options: [{ - pattern: 'path/to/{{name}}.md', - }], + options: [ + { + pattern: 'path/to/{{name}}.md', + }, + ], }, { // Can't determine `url` value statically. @@ -126,9 +134,11 @@ tester.run('require-meta-docs-url', rule, { create() {} } `, - options: [{ - pattern: 'path/to/{{name}}.md', - }], + options: [ + { + pattern: 'path/to/{{name}}.md', + }, + ], }, { // Can't determine `url` value statically. @@ -139,9 +149,11 @@ tester.run('require-meta-docs-url', rule, { create() {} } `, - options: [{ - pattern: 'path/to/{{name}}.md', - }], + options: [ + { + pattern: 'path/to/{{name}}.md', + }, + ], }, ], @@ -342,9 +354,11 @@ tester.run('require-meta-docs-url', rule, { module.exports = function(context) { return {}; } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'FunctionExpression' }], }, { @@ -355,9 +369,11 @@ tester.run('require-meta-docs-url', rule, { } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'Identifier' }], }, { @@ -368,9 +384,11 @@ tester.run('require-meta-docs-url', rule, { } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'Literal' }], }, { @@ -381,9 +399,11 @@ tester.run('require-meta-docs-url', rule, { } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, { @@ -396,9 +416,11 @@ tester.run('require-meta-docs-url', rule, { } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, { @@ -411,9 +433,11 @@ tester.run('require-meta-docs-url', rule, { } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, { @@ -426,9 +450,11 @@ tester.run('require-meta-docs-url', rule, { } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'Identifier' }], }, { @@ -441,9 +467,11 @@ tester.run('require-meta-docs-url', rule, { } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, { @@ -458,9 +486,11 @@ tester.run('require-meta-docs-url', rule, { } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, { @@ -475,9 +505,11 @@ tester.run('require-meta-docs-url', rule, { } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, { @@ -492,9 +524,11 @@ tester.run('require-meta-docs-url', rule, { } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'wrongType', type: 'Literal' }], }, { @@ -509,9 +543,11 @@ tester.run('require-meta-docs-url', rule, { } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, @@ -524,9 +560,11 @@ tester.run('require-meta-docs-url', rule, { module.exports = function(context) { return {}; } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'FunctionExpression' }], }, { @@ -538,9 +576,11 @@ tester.run('require-meta-docs-url', rule, { } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'Identifier' }], }, { @@ -552,9 +592,11 @@ tester.run('require-meta-docs-url', rule, { } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'Literal' }], }, { @@ -575,9 +617,11 @@ url: "plugin-name/test.md" create() {} } `, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, { @@ -599,9 +643,11 @@ url: "plugin-name/test.md" create() {} } `, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, { @@ -623,9 +669,11 @@ url: "plugin-name/test.md" create() {} } `, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], parserOptions: { sourceType: 'module' }, errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, @@ -673,9 +721,11 @@ url: "plugin-name/test.md" create() {} } `, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, { @@ -699,9 +749,11 @@ url: "plugin-name/test.md" create() {} } `, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, { @@ -715,9 +767,11 @@ url: "plugin-name/test.md" } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'Identifier' }], }, { @@ -740,9 +794,11 @@ url: "plugin-name/test.md" create() {} } `, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, { @@ -768,9 +824,11 @@ url: "plugin-name/test.md" create() {} } `, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, { @@ -796,9 +854,11 @@ url: "plugin-name/test.md", create() {} } `, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, { @@ -823,10 +883,17 @@ url: "plugin-name/test.md", create() {} } `, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], - errors: [{ message: '`meta.docs.url` property must be `plugin-name/test.md`.', type: 'Literal' }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], + errors: [ + { + message: '`meta.docs.url` property must be `plugin-name/test.md`.', + type: 'Literal', + }, + ], }, { // `url` in variable, can't autofix it. @@ -841,10 +908,17 @@ url: "plugin-name/test.md", } `, output: null, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], - errors: [{ message: '`meta.docs.url` property must be `plugin-name/test.md`.', type: 'Identifier' }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], + errors: [ + { + message: '`meta.docs.url` property must be `plugin-name/test.md`.', + type: 'Identifier', + }, + ], }, { // `url` is `null`. @@ -865,10 +939,17 @@ url: "plugin-name/test.md", create() {} } `, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], - errors: [{ message: '`meta.docs.url` property must be `plugin-name/test.md`.', type: 'Literal' }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], + errors: [ + { + message: '`meta.docs.url` property must be `plugin-name/test.md`.', + type: 'Literal', + }, + ], }, { // `url` is `undefined`. @@ -889,10 +970,17 @@ url: "plugin-name/test.md", create() {} } `, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], - errors: [{ message: '`meta.docs.url` property must be `plugin-name/test.md`.', type: 'Identifier' }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], + errors: [ + { + message: '`meta.docs.url` property must be `plugin-name/test.md`.', + type: 'Identifier', + }, + ], }, { filename: 'test.js', @@ -917,9 +1005,11 @@ url: "plugin-name/test.md" create() {} } `, - options: [{ - pattern: 'plugin-name/{{ name }}.md', - }], + options: [ + { + pattern: 'plugin-name/{{ name }}.md', + }, + ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], }, ], diff --git a/tests/lib/rules/require-meta-has-suggestions.js b/tests/lib/rules/require-meta-has-suggestions.js index bf81e539..8c357f1e 100644 --- a/tests/lib/rules/require-meta-has-suggestions.js +++ b/tests/lib/rules/require-meta-has-suggestions.js @@ -196,7 +196,16 @@ ruleTester.run('require-meta-has-suggestions', rule, { }; `, output: null, - errors: [{ messageId: 'shouldBeSuggestable', type: 'FunctionExpression', line: 3, column: 17, endLine: 3, endColumn: 78 }], + errors: [ + { + messageId: 'shouldBeSuggestable', + type: 'FunctionExpression', + line: 3, + column: 17, + endLine: 3, + endColumn: 78, + }, + ], }, { // `create` as variable. @@ -205,7 +214,16 @@ ruleTester.run('require-meta-has-suggestions', rule, { module.exports = { create }; `, output: null, - errors: [{ messageId: 'shouldBeSuggestable', type: 'FunctionDeclaration', line: 2, column: 8, endLine: 2, endColumn: 84 }], + errors: [ + { + messageId: 'shouldBeSuggestable', + type: 'FunctionDeclaration', + line: 2, + column: 8, + endLine: 2, + endColumn: 84, + }, + ], }, { // ESM: Reports suggestions, no meta object, violation should be on `create` function. @@ -216,7 +234,16 @@ ruleTester.run('require-meta-has-suggestions', rule, { `, output: null, parserOptions: { sourceType: 'module' }, - errors: [{ messageId: 'shouldBeSuggestable', type: 'FunctionExpression', line: 3, column: 17, endLine: 3, endColumn: 78 }], + errors: [ + { + messageId: 'shouldBeSuggestable', + type: 'FunctionExpression', + line: 3, + column: 17, + endLine: 3, + endColumn: 78, + }, + ], }, { // Reports suggestions, no hasSuggestions property, violation should be on `meta` object, empty meta object. @@ -232,7 +259,16 @@ ruleTester.run('require-meta-has-suggestions', rule, { create(context) { context.report({node, message, suggest: [{}]}); } }; `, - errors: [{ messageId: 'shouldBeSuggestable', type: 'ObjectExpression', line: 3, column: 17, endLine: 3, endColumn: 19 }], + errors: [ + { + messageId: 'shouldBeSuggestable', + type: 'ObjectExpression', + line: 3, + column: 17, + endLine: 3, + endColumn: 19, + }, + ], }, { // Reports suggestions, no hasSuggestions property, violation should be on `meta` object, non-empty meta object. @@ -248,7 +284,16 @@ ruleTester.run('require-meta-has-suggestions', rule, { create(context) { context.report({node, message, suggest: [{}]}); } }; `, - errors: [{ messageId: 'shouldBeSuggestable', type: 'ObjectExpression', line: 3, column: 17, endLine: 3, endColumn: 31 }], + errors: [ + { + messageId: 'shouldBeSuggestable', + type: 'ObjectExpression', + line: 3, + column: 17, + endLine: 3, + endColumn: 31, + }, + ], }, { // Reports suggestions (in variable), no hasSuggestions property, violation should be on `meta` object. @@ -266,7 +311,16 @@ ruleTester.run('require-meta-has-suggestions', rule, { create(context) { context.report({node, message, suggest: SUGGESTIONS}); } }; `, - errors: [{ messageId: 'shouldBeSuggestable', type: 'ObjectExpression', line: 4, column: 17, endLine: 4, endColumn: 19 }], + errors: [ + { + messageId: 'shouldBeSuggestable', + type: 'ObjectExpression', + line: 4, + column: 17, + endLine: 4, + endColumn: 19, + }, + ], }, { // Reports suggestions (in variable, with pushing), no hasSuggestions property, violation should be on `meta` object. @@ -286,7 +340,16 @@ ruleTester.run('require-meta-has-suggestions', rule, { create(context) { context.report({node, message, suggest}); } }; `, - errors: [{ messageId: 'shouldBeSuggestable', type: 'ObjectExpression', line: 5, column: 17, endLine: 5, endColumn: 19 }], + errors: [ + { + messageId: 'shouldBeSuggestable', + type: 'ObjectExpression', + line: 5, + column: 17, + endLine: 5, + endColumn: 19, + }, + ], }, { // Reports suggestions, hasSuggestions property set to false, violation should be on `false` @@ -302,7 +365,16 @@ ruleTester.run('require-meta-has-suggestions', rule, { create(context) { context.report({node, message, suggest: [{}]}); } }; `, - errors: [{ messageId: 'shouldBeSuggestable', type: 'Literal', line: 3, column: 35, endLine: 3, endColumn: 40 }], + errors: [ + { + messageId: 'shouldBeSuggestable', + type: 'Literal', + line: 3, + column: 35, + endLine: 3, + endColumn: 40, + }, + ], }, { // Reports suggestions, hasSuggestions property set to `null`, violation should be on `null` @@ -318,7 +390,16 @@ ruleTester.run('require-meta-has-suggestions', rule, { create(context) { context.report({node, message, suggest: [{}]}); } }; `, - errors: [{ messageId: 'shouldBeSuggestable', type: 'Literal', line: 3, column: 35, endLine: 3, endColumn: 39 }], + errors: [ + { + messageId: 'shouldBeSuggestable', + type: 'Literal', + line: 3, + column: 35, + endLine: 3, + endColumn: 39, + }, + ], }, { // Reports suggestions, hasSuggestions property set to `undefined`, violation should be on `undefined` @@ -334,7 +415,16 @@ ruleTester.run('require-meta-has-suggestions', rule, { create(context) { context.report({node, message, suggest: [{}]}); } }; `, - errors: [{ messageId: 'shouldBeSuggestable', type: 'Identifier', line: 3, column: 35, endLine: 3, endColumn: 44 }], + errors: [ + { + messageId: 'shouldBeSuggestable', + type: 'Identifier', + line: 3, + column: 35, + endLine: 3, + endColumn: 44, + }, + ], }, { // Reports suggestions, hasSuggestions property set to false (as variable), violation should be on variable @@ -346,7 +436,16 @@ ruleTester.run('require-meta-has-suggestions', rule, { }; `, output: null, - errors: [{ messageId: 'shouldBeSuggestable', type: 'Identifier', line: 4, column: 19, endLine: 4, endColumn: 33 }], + errors: [ + { + messageId: 'shouldBeSuggestable', + type: 'Identifier', + line: 4, + column: 19, + endLine: 4, + endColumn: 33, + }, + ], }, { // Does not report suggestions, hasSuggestions property set to true, violation should be on `true` @@ -357,7 +456,16 @@ ruleTester.run('require-meta-has-suggestions', rule, { }; `, output: null, - errors: [{ messageId: 'shouldNotBeSuggestable', type: 'Literal', line: 3, column: 35, endLine: 3, endColumn: 39 }], + errors: [ + { + messageId: 'shouldNotBeSuggestable', + type: 'Literal', + line: 3, + column: 35, + endLine: 3, + endColumn: 39, + }, + ], }, ], }); diff --git a/tests/lib/rules/require-meta-schema.js b/tests/lib/rules/require-meta-schema.js index 3cb523ea..789aff94 100644 --- a/tests/lib/rules/require-meta-schema.js +++ b/tests/lib/rules/require-meta-schema.js @@ -54,7 +54,7 @@ ruleTester.run('require-meta-schema', rule, { }; `, { - // ESM + // ESM code: ` export default { meta: { schema: { "enum": ["always", "never"] } }, @@ -135,8 +135,10 @@ schema: [] create(context) {} }; `, - }], - }], + }, + ], + }, + ], }, { // No `meta`. Violation on `create`. @@ -306,7 +308,9 @@ schema: [] }, }; `, output: null, - errors: [{ messageId: 'foundOptionsUsage', type: 'Property', suggestions: [] }], + errors: [ + { messageId: 'foundOptionsUsage', type: 'Property', suggestions: [] }, + ], }, { // Empty schema (object), but using rule options. @@ -317,7 +321,9 @@ schema: [] }, }; `, output: null, - errors: [{ messageId: 'foundOptionsUsage', type: 'Property', suggestions: [] }], + errors: [ + { messageId: 'foundOptionsUsage', type: 'Property', suggestions: [] }, + ], }, { // Empty schema (object), but using rule options, requireSchemaPropertyWhenOptionless = false. @@ -329,7 +335,9 @@ schema: [] }, `, output: null, options: [{ requireSchemaPropertyWhenOptionless: false }], - errors: [{ messageId: 'foundOptionsUsage', type: 'Property', suggestions: [] }], + errors: [ + { messageId: 'foundOptionsUsage', type: 'Property', suggestions: [] }, + ], }, { // No schema, but using rule options, requireSchemaPropertyWhenOptionless = false. @@ -341,7 +349,13 @@ schema: [] }, `, output: null, options: [{ requireSchemaPropertyWhenOptionless: false }], - errors: [{ messageId: 'foundOptionsUsage', type: 'ObjectExpression', suggestions: [] }], + errors: [ + { + messageId: 'foundOptionsUsage', + type: 'ObjectExpression', + suggestions: [], + }, + ], }, { // No schema, but using rule options, should have no suggestions. @@ -353,7 +367,11 @@ schema: [] }, `, output: null, errors: [ - { messageId: 'foundOptionsUsage', type: 'ObjectExpression', suggestions: [] }, + { + messageId: 'foundOptionsUsage', + type: 'ObjectExpression', + suggestions: [], + }, { messageId: 'missing', type: 'ObjectExpression', suggestions: [] }, ], }, @@ -366,7 +384,11 @@ schema: [] }, `, output: null, errors: [ - { messageId: 'foundOptionsUsage', type: 'ObjectExpression', suggestions: [] }, + { + messageId: 'foundOptionsUsage', + type: 'ObjectExpression', + suggestions: [], + }, { messageId: 'missing', type: 'ObjectExpression', suggestions: [] }, ], }, @@ -380,7 +402,11 @@ schema: [] }, `, output: null, errors: [ - { messageId: 'foundOptionsUsage', type: 'ObjectExpression', suggestions: [] }, + { + messageId: 'foundOptionsUsage', + type: 'ObjectExpression', + suggestions: [], + }, { messageId: 'missing', type: 'ObjectExpression', suggestions: [] }, ], }, diff --git a/tests/lib/rules/test-case-property-ordering.js b/tests/lib/rules/test-case-property-ordering.js index 3a5a3d6b..c46300e2 100644 --- a/tests/lib/rules/test-case-property-ordering.js +++ b/tests/lib/rules/test-case-property-ordering.js @@ -76,7 +76,12 @@ ruleTester.run('test-case-property-ordering', rule, { ] }); `, - errors: [{ message: 'The properties of a test case should be placed in a consistent order: [code, output, options].' }], + errors: [ + { + message: + 'The properties of a test case should be placed in a consistent order: [code, output, options].', + }, + ], }, { code: ` @@ -93,7 +98,12 @@ ruleTester.run('test-case-property-ordering', rule, { ] }); `, - errors: [{ message: 'The properties of a test case should be placed in a consistent order: [code, output, options, env].' }], + errors: [ + { + message: + 'The properties of a test case should be placed in a consistent order: [code, output, options, env].', + }, + ], }, { code: ` @@ -110,7 +120,12 @@ ruleTester.run('test-case-property-ordering', rule, { ] }); `, - errors: [{ message: 'The properties of a test case should be placed in a consistent order: [code, output, options, env].' }], + errors: [ + { + message: + 'The properties of a test case should be placed in a consistent order: [code, output, options, env].', + }, + ], }, { code: ` @@ -128,7 +143,12 @@ ruleTester.run('test-case-property-ordering', rule, { }); `, options: [['code', 'errors', 'options', 'output', 'parserOptions']], - errors: [{ message: 'The properties of a test case should be placed in a consistent order: [code, options, output].' }], + errors: [ + { + message: + 'The properties of a test case should be placed in a consistent order: [code, options, output].', + }, + ], }, { code: ` @@ -145,7 +165,12 @@ ruleTester.run('test-case-property-ordering', rule, { ] }); `, - errors: [{ message: 'The properties of a test case should be placed in a consistent order: [code, output, parserOptions, errors].' }], + errors: [ + { + message: + 'The properties of a test case should be placed in a consistent order: [code, output, parserOptions, errors].', + }, + ], }, ], }); diff --git a/tests/lib/rules/test-case-shorthand-strings.js b/tests/lib/rules/test-case-shorthand-strings.js index cf29c26a..f97b75a2 100644 --- a/tests/lib/rules/test-case-shorthand-strings.js +++ b/tests/lib/rules/test-case-shorthand-strings.js @@ -13,11 +13,11 @@ const rule = require('../../../lib/rules/test-case-shorthand-strings'); const RuleTester = require('eslint').RuleTester; /** -* Returns the code for some valid test cases -* @param {string[]} cases The code representation of valid test cases -* @returns {string} Code representing the test cases -*/ -function getTestCases (cases) { + * Returns the code for some valid test cases + * @param {string[]} cases The code representation of valid test cases + * @returns {string} Code representing the test cases + */ +function getTestCases(cases) { return ` new RuleTester().run('foo', bar, { valid: [ @@ -28,8 +28,13 @@ function getTestCases (cases) { `; } -const EXPECTED_SHORTHAND_ERROR = { message: 'Use a string for this test case instead of an object.', type: 'ObjectExpression' }; -const UNEXPECTED_SHORTHAND_ERROR = { message: 'Use an object for this test case instead of a string.' }; +const EXPECTED_SHORTHAND_ERROR = { + message: 'Use a string for this test case instead of an object.', + type: 'ObjectExpression', +}; +const UNEXPECTED_SHORTHAND_ERROR = { + message: 'Use an object for this test case instead of a string.', +}; // ------------------------------------------------------------------------------ // Tests @@ -37,9 +42,7 @@ const UNEXPECTED_SHORTHAND_ERROR = { message: 'Use an object for this test case const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } }); ruleTester.run('test-case-shorthand-strings', rule, { - valid: [ - // default (as-needed) getTestCases(['"foo"']), getTestCases(['"foo"', '"bar"']), @@ -58,11 +61,19 @@ ruleTester.run('test-case-shorthand-strings', rule, { options: ['as-needed'], }, { - code: getTestCases(['"foo"', '"bar"', '{ code: "foo", options: ["bar"] }']), + code: getTestCases([ + '"foo"', + '"bar"', + '{ code: "foo", options: ["bar"] }', + ]), options: ['as-needed'], }, { - code: getTestCases(['"foo"', '"bar"', '{ code: "foo", parserOptions: ["bar"] }']), + code: getTestCases([ + '"foo"', + '"bar"', + '{ code: "foo", parserOptions: ["bar"] }', + ]), options: ['as-needed'], }, { @@ -98,7 +109,10 @@ ruleTester.run('test-case-shorthand-strings', rule, { options: ['consistent'], }, { - code: getTestCases(['{ code: "foo" }', '{ code: "bar", options: ["foo"] }']), + code: getTestCases([ + '{ code: "foo" }', + '{ code: "bar", options: ["foo"] }', + ]), options: ['consistent'], }, { @@ -112,7 +126,10 @@ ruleTester.run('test-case-shorthand-strings', rule, { options: ['consistent-as-needed'], }, { - code: getTestCases(['{ code: "foo" }', '{ code: "bar", options: ["foo"] }']), + code: getTestCases([ + '{ code: "foo" }', + '{ code: "bar", options: ["foo"] }', + ]), options: ['consistent-as-needed'], }, { @@ -122,7 +139,6 @@ ruleTester.run('test-case-shorthand-strings', rule, { ], invalid: [ - // as-needed { code: getTestCases(['{ code: "foo" }']), @@ -191,14 +207,30 @@ ruleTester.run('test-case-shorthand-strings', rule, { errors: [EXPECTED_SHORTHAND_ERROR, EXPECTED_SHORTHAND_ERROR], }, { - code: getTestCases(['"foo"', '"bar"', '{ code: "baz", options: ["foo"] }']), - output: getTestCases(['{code: "foo"}', '{code: "bar"}', '{ code: "baz", options: ["foo"] }']), + code: getTestCases([ + '"foo"', + '"bar"', + '{ code: "baz", options: ["foo"] }', + ]), + output: getTestCases([ + '{code: "foo"}', + '{code: "bar"}', + '{ code: "baz", options: ["foo"] }', + ]), options: ['consistent-as-needed'], errors: [UNEXPECTED_SHORTHAND_ERROR, UNEXPECTED_SHORTHAND_ERROR], }, { - code: getTestCases(['"foo"', '{ code: "baz", options: ["foo"] }', '"bar"']), - output: getTestCases(['{code: "foo"}', '{ code: "baz", options: ["foo"] }', '{code: "bar"}']), + code: getTestCases([ + '"foo"', + '{ code: "baz", options: ["foo"] }', + '"bar"', + ]), + output: getTestCases([ + '{code: "foo"}', + '{ code: "baz", options: ["foo"] }', + '{code: "bar"}', + ]), options: ['consistent-as-needed'], errors: [UNEXPECTED_SHORTHAND_ERROR, UNEXPECTED_SHORTHAND_ERROR], }, diff --git a/tests/lib/utils.js b/tests/lib/utils.js index a87862af..8a45790b 100644 --- a/tests/lib/utils.js +++ b/tests/lib/utils.js @@ -50,11 +50,14 @@ describe('utils', () => { 'module.exports = createESLintRule({ create() {}, meta: {} });', 'module.exports = util.createRule({ create() {}, meta: {} });', 'module.exports = ESLintUtils.RuleCreator(docsUrl)({ create() {}, meta: {} });', - ].forEach(noRuleCase => { + ].forEach((noRuleCase) => { it(`returns null for ${noRuleCase}`, () => { const ast = espree.parse(noRuleCase, { ecmaVersion: 8, range: true }); const scopeManager = eslintScope.analyze(ast); - assert.isNull(utils.getRuleInfo({ ast, scopeManager }), 'Expected no rule to be found'); + assert.isNull( + utils.getRuleInfo({ ast, scopeManager }), + 'Expected no rule to be found' + ); }); }); }); @@ -87,12 +90,18 @@ describe('utils', () => { 'export default foo(123);', 'export default foo.bar(123);', 'export default foo.bar()(123);', - - ].forEach(noRuleCase => { + ].forEach((noRuleCase) => { it(`returns null for ${noRuleCase}`, () => { - const ast = espree.parse(noRuleCase, { ecmaVersion: 8, range: true, sourceType: 'module' }); + const ast = espree.parse(noRuleCase, { + ecmaVersion: 8, + range: true, + sourceType: 'module', + }); const scopeManager = eslintScope.analyze(ast); - assert.isNull(utils.getRuleInfo({ ast, scopeManager }), 'Expected no rule to be found'); + assert.isNull( + utils.getRuleInfo({ ast, scopeManager }), + 'Expected no rule to be found' + ); }); }); }); @@ -107,11 +116,18 @@ describe('utils', () => { 'export default foo.bar(123);', 'export default foo.bar()(123);', 'const notRule = foo(); export default notRule;', - ].forEach(noRuleCase => { + ].forEach((noRuleCase) => { it(`returns null for ${noRuleCase}`, () => { - const ast = typescriptEslintParser.parse(noRuleCase, { ecmaVersion: 8, range: true, sourceType: 'module' }); + const ast = typescriptEslintParser.parse(noRuleCase, { + ecmaVersion: 8, + range: true, + sourceType: 'module', + }); const scopeManager = eslintScope.analyze(ast); - assert.isNull(utils.getRuleInfo({ ast, scopeManager }), 'Expected no rule to be found'); + assert.isNull( + utils.getRuleInfo({ ast, scopeManager }), + 'Expected no rule to be found' + ); }); }); }); @@ -122,11 +138,17 @@ describe('utils', () => { 'module.exports = createESLintRule({ create() {}, meta: {} });', 'module.exports = util.createRule({ create() {}, meta: {} });', 'module.exports = ESLintUtils.RuleCreator(docsUrl)({ create() {}, meta: {} });', - ].forEach(noRuleCase => { + ].forEach((noRuleCase) => { it(`returns null for ${noRuleCase}`, () => { - const ast = typescriptEslintParser.parse(noRuleCase, { range: true, sourceType: 'script' }); + const ast = typescriptEslintParser.parse(noRuleCase, { + range: true, + sourceType: 'script', + }); const scopeManager = eslintScope.analyze(ast); - assert.isNull(utils.getRuleInfo({ ast, scopeManager }), 'Expected no rule to be found'); + assert.isNull( + utils.getRuleInfo({ ast, scopeManager }), + 'Expected no rule to be found' + ); }); }); }); @@ -134,11 +156,12 @@ describe('utils', () => { describe('the file has a valid rule (TypeScript + TypeScript parser + ESM)', () => { const CASES = { // Util function only - 'export default createESLintRule({ create() {}, meta: {} });': { - create: { type: 'FunctionExpression' }, - meta: { type: 'ObjectExpression' }, - isNewStyle: true, - }, + 'export default createESLintRule({ create() {}, meta: {} });': + { + create: { type: 'FunctionExpression' }, + meta: { type: 'ObjectExpression' }, + isNewStyle: true, + }, 'export default createESLintRule<>({ create() {}, meta: {} });': { create: { type: 'FunctionExpression' }, meta: { type: 'ObjectExpression' }, @@ -149,30 +172,34 @@ describe('utils', () => { meta: { type: 'ObjectExpression' }, isNewStyle: true, }, - 'const create = context => {}; const meta = {}; export default createESLintRule({ create, meta });': { - create: { type: 'ArrowFunctionExpression' }, - meta: { type: 'ObjectExpression' }, - isNewStyle: true, - }, - 'const rule = createESLintRule({ create() {}, meta: {} }); export default rule;': { - create: { type: 'FunctionExpression' }, - meta: { type: 'ObjectExpression' }, - isNewStyle: true, - }, + 'const create = context => {}; const meta = {}; export default createESLintRule({ create, meta });': + { + create: { type: 'ArrowFunctionExpression' }, + meta: { type: 'ObjectExpression' }, + isNewStyle: true, + }, + 'const rule = createESLintRule({ create() {}, meta: {} }); export default rule;': + { + create: { type: 'FunctionExpression' }, + meta: { type: 'ObjectExpression' }, + isNewStyle: true, + }, // Util function with "{} as const". - 'export default createESLintRule({ create() {}, meta: {} as const });': { - create: { type: 'FunctionExpression' }, - meta: { type: 'ObjectExpression' }, - isNewStyle: true, - }, + 'export default createESLintRule({ create() {}, meta: {} as const });': + { + create: { type: 'FunctionExpression' }, + meta: { type: 'ObjectExpression' }, + isNewStyle: true, + }, // Util function from util object - 'export default util.createRule({ create() {}, meta: {} });': { - create: { type: 'FunctionExpression' }, - meta: { type: 'ObjectExpression' }, - isNewStyle: true, - }, + 'export default util.createRule({ create() {}, meta: {} });': + { + create: { type: 'FunctionExpression' }, + meta: { type: 'ObjectExpression' }, + isNewStyle: true, + }, 'export default util.createRule({ create() {}, meta: {} });': { create: { type: 'FunctionExpression' }, meta: { type: 'ObjectExpression' }, @@ -180,26 +207,34 @@ describe('utils', () => { }, // Util function from util object with additional doc URL argument - 'export default ESLintUtils.RuleCreator(docsUrl)({ create() {}, meta: {} });': { - create: { type: 'FunctionExpression' }, - meta: { type: 'ObjectExpression' }, - isNewStyle: true, - }, - 'export default ESLintUtils.RuleCreator(docsUrl)({ create() {}, meta: {} });': { - create: { type: 'FunctionExpression' }, - meta: { type: 'ObjectExpression' }, - isNewStyle: true, - }, + 'export default ESLintUtils.RuleCreator(docsUrl)({ create() {}, meta: {} });': + { + create: { type: 'FunctionExpression' }, + meta: { type: 'ObjectExpression' }, + isNewStyle: true, + }, + 'export default ESLintUtils.RuleCreator(docsUrl)({ create() {}, meta: {} });': + { + create: { type: 'FunctionExpression' }, + meta: { type: 'ObjectExpression' }, + isNewStyle: true, + }, }; - Object.keys(CASES).forEach(ruleSource => { + Object.keys(CASES).forEach((ruleSource) => { it(ruleSource, () => { - const ast = typescriptEslintParser.parse(ruleSource, { ecmaVersion: 6, range: true, sourceType: 'module' }); + const ast = typescriptEslintParser.parse(ruleSource, { + ecmaVersion: 6, + range: true, + sourceType: 'module', + }); const scopeManager = eslintScope.analyze(ast); const ruleInfo = utils.getRuleInfo({ ast, scopeManager }); assert( lodash.isMatch(ruleInfo, CASES[ruleSource]), - `Expected \n${inspect(ruleInfo)}\nto match\n${inspect(CASES[ruleSource])}` + `Expected \n${inspect(ruleInfo)}\nto match\n${inspect( + CASES[ruleSource] + )}` ); }); }); @@ -222,21 +257,23 @@ describe('utils', () => { meta: { type: 'ObjectExpression' }, isNewStyle: true, }, - 'module.exports.create = function foo(context) {}; module.exports.meta = {}': { - create: { type: 'FunctionExpression', id: { name: 'foo' } }, - meta: { type: 'ObjectExpression' }, - isNewStyle: true, - }, + 'module.exports.create = function foo(context) {}; module.exports.meta = {}': + { + create: { type: 'FunctionExpression', id: { name: 'foo' } }, + meta: { type: 'ObjectExpression' }, + isNewStyle: true, + }, 'exports.create = function foo() {}; exports.meta = {};': { create: { type: 'FunctionExpression', id: { name: 'foo' } }, meta: { type: 'ObjectExpression' }, isNewStyle: true, }, - 'module.exports = { create: () => { } }; exports.create = function foo() {}; exports.meta = {};': { - create: { type: 'ArrowFunctionExpression' }, - meta: null, - isNewStyle: true, - }, + 'module.exports = { create: () => { } }; exports.create = function foo() {}; exports.meta = {};': + { + create: { type: 'ArrowFunctionExpression' }, + meta: null, + isNewStyle: true, + }, 'exports.meta = {}; module.exports = { create: () => { } };': { create: { type: 'ArrowFunctionExpression' }, meta: null, @@ -267,11 +304,12 @@ describe('utils', () => { meta: null, isNewStyle: false, }, - 'module.exports = function foo(slightlyDifferentContextName) { return {}; }': { - create: { type: 'FunctionExpression', id: { name: 'foo' } }, - meta: null, - isNewStyle: false, - }, + 'module.exports = function foo(slightlyDifferentContextName) { return {}; }': + { + create: { type: 'FunctionExpression', id: { name: 'foo' } }, + meta: null, + isNewStyle: false, + }, 'module.exports = function foo({ report }) { return {}; }': { create: { type: 'FunctionExpression', id: { name: 'foo' } }, meta: null, @@ -292,16 +330,18 @@ describe('utils', () => { meta: null, isNewStyle: false, }, - 'module.exports = (context) => { return {}; }; module.exports.meta = {};': { - create: { type: 'ArrowFunctionExpression' }, - meta: null, - isNewStyle: false, - }, - 'const create = function(context) { return {}; }; const meta = {}; module.exports = { create, meta };': { - create: { type: 'FunctionExpression' }, - meta: { type: 'ObjectExpression' }, - isNewStyle: true, - }, + 'module.exports = (context) => { return {}; }; module.exports.meta = {};': + { + create: { type: 'ArrowFunctionExpression' }, + meta: null, + isNewStyle: false, + }, + 'const create = function(context) { return {}; }; const meta = {}; module.exports = { create, meta };': + { + create: { type: 'FunctionExpression' }, + meta: { type: 'ObjectExpression' }, + isNewStyle: true, + }, 'const rule = { create() {}, meta: {} }; module.exports = rule;': { create: { type: 'FunctionExpression' }, meta: { type: 'ObjectExpression' }, @@ -309,14 +349,20 @@ describe('utils', () => { }, }; - Object.keys(CASES).forEach(ruleSource => { + Object.keys(CASES).forEach((ruleSource) => { it(ruleSource, () => { - const ast = espree.parse(ruleSource, { ecmaVersion: 6, range: true, sourceType: 'script' }); + const ast = espree.parse(ruleSource, { + ecmaVersion: 6, + range: true, + sourceType: 'script', + }); const scopeManager = eslintScope.analyze(ast); const ruleInfo = utils.getRuleInfo({ ast, scopeManager }); assert( lodash.isMatch(ruleInfo, CASES[ruleSource]), - `Expected \n${inspect(ruleInfo)}\nto match\n${inspect(CASES[ruleSource])}` + `Expected \n${inspect(ruleInfo)}\nto match\n${inspect( + CASES[ruleSource] + )}` ); }); }); @@ -335,26 +381,29 @@ describe('utils', () => { meta: { type: 'ObjectExpression' }, isNewStyle: true, }, - 'const create = function(context) { return {}; }; const meta = {}; export default { create, meta }': { - create: { type: 'FunctionExpression' }, - meta: { type: 'ObjectExpression' }, - isNewStyle: true, - }, - 'function create(context) { return {}; }; const meta = {}; export default { create, meta }': { - create: { type: 'FunctionDeclaration' }, - meta: { type: 'ObjectExpression' }, - isNewStyle: true, - }, + 'const create = function(context) { return {}; }; const meta = {}; export default { create, meta }': + { + create: { type: 'FunctionExpression' }, + meta: { type: 'ObjectExpression' }, + isNewStyle: true, + }, + 'function create(context) { return {}; }; const meta = {}; export default { create, meta }': + { + create: { type: 'FunctionDeclaration' }, + meta: { type: 'ObjectExpression' }, + isNewStyle: true, + }, 'const rule = { create() {}, meta: {} }; export default rule;': { create: { type: 'FunctionExpression' }, meta: { type: 'ObjectExpression' }, isNewStyle: true, }, - 'const create = function() {}; const meta = {}; const rule = { create, meta }; export default rule;': { - create: { type: 'FunctionExpression' }, - meta: { type: 'ObjectExpression' }, - isNewStyle: true, - }, + 'const create = function() {}; const meta = {}; const rule = { create, meta }; export default rule;': + { + create: { type: 'FunctionExpression' }, + meta: { type: 'ObjectExpression' }, + isNewStyle: true, + }, // ESM (function style) 'export default function (context) { return {}; }': { @@ -374,14 +423,20 @@ describe('utils', () => { }, }; - Object.keys(CASES).forEach(ruleSource => { + Object.keys(CASES).forEach((ruleSource) => { it(ruleSource, () => { - const ast = espree.parse(ruleSource, { ecmaVersion: 6, range: true, sourceType: 'module' }); + const ast = espree.parse(ruleSource, { + ecmaVersion: 6, + range: true, + sourceType: 'module', + }); const scopeManager = eslintScope.analyze(ast); const ruleInfo = utils.getRuleInfo({ ast, scopeManager }); assert( lodash.isMatch(ruleInfo, CASES[ruleSource]), - `Expected \n${inspect(ruleInfo)}\nto match\n${inspect(CASES[ruleSource])}` + `Expected \n${inspect(ruleInfo)}\nto match\n${inspect( + CASES[ruleSource] + )}` ); }); }); @@ -389,15 +444,23 @@ describe('utils', () => { describe('the file has a valid rule (different scope options)', () => { for (const scopeOptions of [ - { ignoreEval: true, ecmaVersion: 6, sourceType: 'script', nodejsScope: true }, + { + ignoreEval: true, + ecmaVersion: 6, + sourceType: 'script', + nodejsScope: true, + }, { ignoreEval: true, ecmaVersion: 6, sourceType: 'script' }, { ignoreEval: true, ecmaVersion: 6, sourceType: 'module' }, ]) { - const ast = espree.parse(` + const ast = espree.parse( + ` const create = (context) => {}; const meta = {}; module.exports = { create, meta }; - `, { ecmaVersion: 6, range: true }); + `, + { ecmaVersion: 6, range: true } + ); const expected = { create: { type: 'ArrowFunctionExpression' }, meta: { type: 'ObjectExpression' }, @@ -417,7 +480,8 @@ describe('utils', () => { describe('the file has newer syntax', () => { const CASES = [ { - source: 'module.exports = function(context) { class Foo { @someDecorator() someProp }; return {}; };', + source: + 'module.exports = function(context) { class Foo { @someDecorator() someProp }; return {}; };', options: { sourceType: 'script' }, expected: { create: { type: 'FunctionExpression' }, @@ -426,7 +490,8 @@ describe('utils', () => { }, }, { - source: 'export default function(context) { class Foo { @someDecorator() someProp }; return {}; };', + source: + 'export default function(context) { class Foo { @someDecorator() someProp }; return {}; };', options: { sourceType: 'module' }, expected: { create: { type: 'FunctionDeclaration' }, @@ -438,12 +503,17 @@ describe('utils', () => { for (const testCase of CASES) { describe(testCase.source, () => { it('does not throw with node type PropertyDefinition which is not handled by estraverse (estraverse is used for detecting the object return statement in a function-style rule).', () => { - const ast = typescriptEslintParser.parse(testCase.source, testCase.options); + const ast = typescriptEslintParser.parse( + testCase.source, + testCase.options + ); const scopeManager = eslintScope.analyze(ast); const ruleInfo = utils.getRuleInfo({ ast, scopeManager }); assert( lodash.isMatch(ruleInfo, testCase.expected), - `Expected \n${inspect(ruleInfo)}\nto match\n${inspect(testCase.expected)}` + `Expected \n${inspect(ruleInfo)}\nto match\n${inspect( + testCase.expected + )}` ); }); }); @@ -453,38 +523,59 @@ describe('utils', () => { describe('getContextIdentifiers', () => { const CASES = { - 'module.exports = context => { context; context; context; return {}; }' (ast) { + 'module.exports = context => { context; context; context; return {}; }'( + ast + ) { return [ ast.body[0].expression.right.body.body[0].expression, ast.body[0].expression.right.body.body[1].expression, ast.body[0].expression.right.body.body[2].expression, ]; }, - 'module.exports = { meta: {}, create(context, foo = context) {} }' (ast) { - return [ast.body[0].expression.right.properties[1].value.params[1].right]; - }, - 'module.exports = { meta: {}, create(notContext) { notContext; notContext; notContext; } }' (ast) { + 'module.exports = { meta: {}, create(context, foo = context) {} }'(ast) { return [ - ast.body[0].expression.right.properties[1].value.body.body[0].expression, - ast.body[0].expression.right.properties[1].value.body.body[1].expression, - ast.body[0].expression.right.properties[1].value.body.body[2].expression, + ast.body[0].expression.right.properties[1].value.params[1].right, ]; }, - 'const create = function(context) { context }; module.exports = { meta: {}, create };' (ast) { + 'module.exports = { meta: {}, create(notContext) { notContext; notContext; notContext; } }'( + ast + ) { return [ - ast.body[0].declarations[0].init.body.body[0].expression, + ast.body[0].expression.right.properties[1].value.body.body[0] + .expression, + ast.body[0].expression.right.properties[1].value.body.body[1] + .expression, + ast.body[0].expression.right.properties[1].value.body.body[2] + .expression, ]; }, + 'const create = function(context) { context }; module.exports = { meta: {}, create };'( + ast + ) { + return [ast.body[0].declarations[0].init.body.body[0].expression]; + }, }; - Object.keys(CASES).forEach(ruleSource => { + Object.keys(CASES).forEach((ruleSource) => { it(ruleSource, () => { const ast = espree.parse(ruleSource, { ecmaVersion: 6, range: true }); - const scope = eslintScope.analyze(ast, { ignoreEval: true, ecmaVersion: 6, sourceType: 'script', nodejsScope: true }); + const scope = eslintScope.analyze(ast, { + ignoreEval: true, + ecmaVersion: 6, + sourceType: 'script', + nodejsScope: true, + }); const identifiers = utils.getContextIdentifiers(scope, ast); - assert(identifiers instanceof Set, 'getContextIdentifiers should return a Set'); - assert.strictEqual(identifiers.size, CASES[ruleSource](ast).length, 'has the correct number of results'); + assert( + identifiers instanceof Set, + 'getContextIdentifiers should return a Set' + ); + assert.strictEqual( + identifiers.size, + CASES[ruleSource](ast).length, + 'has the correct number of results' + ); [...identifiers].forEach((identifier, index) => { assert.strictEqual(identifier, CASES[ruleSource](ast)[index]); }); @@ -508,22 +599,28 @@ describe('utils', () => { '({ [tag`foo`]: 1 })': null, '({ ["foo" + "bar"]: 1 })': null, }; - Object.keys(CASES).forEach(objectSource => { + Object.keys(CASES).forEach((objectSource) => { it(objectSource, () => { const ast = espree.parse(objectSource, { ecmaVersion: 6, range: true }); - assert.strictEqual(utils.getKeyName(ast.body[0].expression.properties[0]), CASES[objectSource]); + assert.strictEqual( + utils.getKeyName(ast.body[0].expression.properties[0]), + CASES[objectSource] + ); }); }); const CASES_ES9 = { '({ ...foo })': null, }; - Object.keys(CASES_ES9).forEach(objectSource => { + Object.keys(CASES_ES9).forEach((objectSource) => { it(objectSource, () => { const ast = espree.parse(objectSource, { ecmaVersion: 9, range: true }); - assert.strictEqual(utils.getKeyName(ast.body[0].expression.properties[0]), CASES_ES9[objectSource]); + assert.strictEqual( + utils.getKeyName(ast.body[0].expression.properties[0]), + CASES_ES9[objectSource] + ); }); }); }); @@ -541,31 +638,57 @@ describe('utils', () => { 'new RuleTester().run(foo)', 'new RuleTester().run(foo, bar)', 'new RuleTester().run(foo, bar, notAnObject)', - ].forEach(noTestsCase => { + ].forEach((noTestsCase) => { it(`returns no tests for ${noTestsCase}`, () => { - const ast = espree.parse(noTestsCase, { ecmaVersion: 8, range: true }); - const scope = eslintScope.analyze(ast, { ignoreEval: true, ecmaVersion: 6, sourceType: 'script', nodejsScope: true }); - assert.deepEqual(utils.getTestInfo(scope, ast), [], 'Expected no tests to be found'); + const ast = espree.parse(noTestsCase, { + ecmaVersion: 8, + range: true, + }); + const scope = eslintScope.analyze(ast, { + ignoreEval: true, + ecmaVersion: 6, + sourceType: 'script', + nodejsScope: true, + }); + assert.deepEqual( + utils.getTestInfo(scope, ast), + [], + 'Expected no tests to be found' + ); }); }); }); describe('the file has valid tests', () => { const CASES = { - 'new RuleTester().run(bar, baz, { valid: [foo], invalid: [bar, baz] })': { valid: 1, invalid: 2 }, - 'var foo = new RuleTester(); foo.run(bar, baz, { valid: [foo], invalid: [bar] })': { valid: 1, invalid: 1 }, - 'var foo = new (require("eslint")).RuleTester; foo.run(bar, baz, { valid: [], invalid: [] })': { valid: 0, invalid: 0 }, - 'var foo = new bar.RuleTester; foo.run(bar, baz, { valid: [], invalid: [bar, baz] })': { valid: 0, invalid: 2 }, - 'var foo = new bar.RuleTester; foo.run(bar, baz, { valid: [,], invalid: [bar, , baz] })': { valid: 0, invalid: 2 }, + 'new RuleTester().run(bar, baz, { valid: [foo], invalid: [bar, baz] })': + { valid: 1, invalid: 2 }, + 'var foo = new RuleTester(); foo.run(bar, baz, { valid: [foo], invalid: [bar] })': + { valid: 1, invalid: 1 }, + 'var foo = new (require("eslint")).RuleTester; foo.run(bar, baz, { valid: [], invalid: [] })': + { valid: 0, invalid: 0 }, + 'var foo = new bar.RuleTester; foo.run(bar, baz, { valid: [], invalid: [bar, baz] })': + { valid: 0, invalid: 2 }, + 'var foo = new bar.RuleTester; foo.run(bar, baz, { valid: [,], invalid: [bar, , baz] })': + { valid: 0, invalid: 2 }, }; - Object.keys(CASES).forEach(testSource => { + Object.keys(CASES).forEach((testSource) => { it(testSource, () => { const ast = espree.parse(testSource, { ecmaVersion: 6, range: true }); - const scope = eslintScope.analyze(ast, { ignoreEval: true, ecmaVersion: 6, sourceType: 'script', nodejsScope: true }); + const scope = eslintScope.analyze(ast, { + ignoreEval: true, + ecmaVersion: 6, + sourceType: 'script', + nodejsScope: true, + }); const testInfo = utils.getTestInfo(scope, ast); - assert.strictEqual(testInfo.length, 1, 'Expected to find one test run'); + assert.strictEqual( + testInfo.length, + 1, + 'Expected to find one test run' + ); assert.strictEqual( testInfo[0].valid.length, @@ -587,27 +710,40 @@ describe('utils', () => { [` new RuleTester().run(foo, bar, { valid: [foo], invalid: [] }); new RuleTester().run(foo, bar, { valid: [], invalid: [foo, bar] }); - `]: [{ valid: 1, invalid: 0 }, { valid: 0, invalid: 2 }], + `]: [ + { valid: 1, invalid: 0 }, + { valid: 0, invalid: 2 }, + ], [` var foo = new RuleTester; var bar = new RuleTester; foo.run(foo, bar, { valid: [foo, bar, baz], invalid: [foo] }); bar.run(foo, bar, { valid: [], invalid: [foo, bar] }); - `]: [{ valid: 3, invalid: 1 }, { valid: 0, invalid: 2 }], + `]: [ + { valid: 3, invalid: 1 }, + { valid: 0, invalid: 2 }, + ], [` var foo = new RuleTester, bar = new RuleTester; foo.run(foo, bar, { valid: [foo, bar, baz], invalid: [foo] }); bar.run(foo, bar, { valid: [], invalid: [foo, bar] }); - `]: [{ valid: 3, invalid: 1 }, { valid: 0, invalid: 2 }], - + `]: [ + { valid: 3, invalid: 1 }, + { valid: 0, invalid: 2 }, + ], }; - Object.keys(CASES).forEach(testSource => { + Object.keys(CASES).forEach((testSource) => { it(testSource, () => { const ast = espree.parse(testSource, { ecmaVersion: 6, range: true }); - const scope = eslintScope.analyze(ast, { ignoreEval: true, ecmaVersion: 6, sourceType: 'script', nodejsScope: true }); + const scope = eslintScope.analyze(ast, { + ignoreEval: true, + ecmaVersion: 6, + sourceType: 'script', + nodejsScope: true, + }); const testInfo = utils.getTestInfo(scope, ast); assert.strictEqual( @@ -620,12 +756,16 @@ describe('utils', () => { assert.strictEqual( testRun.valid, testInfo[index].valid.length, - `On run ${index + 1}, expected ${testRun.valid} valid cases but got ${testInfo[index].valid.length}` + `On run ${index + 1}, expected ${ + testRun.valid + } valid cases but got ${testInfo[index].valid.length}` ); assert.strictEqual( testRun.invalid, testInfo[index].invalid.length, - `On run ${index + 1}, expected ${testRun.invalid} valid cases but got ${testInfo[index].invalid.length}` + `On run ${index + 1}, expected ${ + testRun.invalid + } valid cases but got ${testInfo[index].invalid.length}` ); }); }); @@ -637,11 +777,33 @@ describe('utils', () => { const CASES = new Map([ [[], () => null], [['foo', 'bar'], () => null], - [['foo', '"bar"', 'baz', 'qux', 'boop'], args => ({ node: args[0], message: args[1], data: args[2], fix: args[3] })], - [['foo', '`bar`', 'baz', 'qux', 'boop'], args => ({ node: args[0], message: args[1], data: args[2], fix: args[3] })], + [ + ['foo', '"bar"', 'baz', 'qux', 'boop'], + (args) => ({ + node: args[0], + message: args[1], + data: args[2], + fix: args[3], + }), + ], + [ + ['foo', '`bar`', 'baz', 'qux', 'boop'], + (args) => ({ + node: args[0], + message: args[1], + data: args[2], + fix: args[3], + }), + ], [ ['foo', '{ bar: 1 }', 'baz', 'qux', 'boop'], - args => ({ node: args[0], loc: args[1], message: args[2], data: args[3], fix: args[4] }), + (args) => ({ + node: args[0], + loc: args[1], + message: args[2], + data: args[3], + fix: args[4], + }), ], [['foo', 'bar', 'baz'], () => null], [ @@ -655,11 +817,12 @@ describe('utils', () => { for (const args of CASES.keys()) { it(args.join(', '), () => { - const parsedArgs = espree.parse( - `context.report(${args.join(', ')})`, - { ecmaVersion: 6, loc: false, range: false } - ).body[0].expression.arguments; - const context = { getScope () {} }; // mock object + const parsedArgs = espree.parse(`context.report(${args.join(', ')})`, { + ecmaVersion: 6, + loc: false, + range: false, + }).body[0].expression.arguments; + const context = { getScope() {} }; // mock object const reportInfo = utils.getReportInfo(parsedArgs, context); assert.deepEqual(reportInfo, CASES.get(args)(parsedArgs)); @@ -674,18 +837,26 @@ describe('utils', () => { 'module.exports = context => { const sourceCode = context.getNotSourceCode(); return {}; }': 0, }; - Object.keys(CASES).forEach(testSource => { + Object.keys(CASES).forEach((testSource) => { it(testSource, () => { const ast = espree.parse(testSource, { ecmaVersion: 6, range: true }); - const scope = eslintScope.analyze(ast, { ignoreEval: true, ecmaVersion: 6, sourceType: 'script', nodejsScope: true }); + const scope = eslintScope.analyze(ast, { + ignoreEval: true, + ecmaVersion: 6, + sourceType: 'script', + nodejsScope: true, + }); estraverse.traverse(ast, { - enter (node, parent) { + enter(node, parent) { node.parent = parent; }, }); - assert.strictEqual(utils.getSourceCodeIdentifiers(scope, ast).size, CASES[testSource]); + assert.strictEqual( + utils.getSourceCodeIdentifiers(scope, ast).size, + CASES[testSource] + ); }); }); }); @@ -713,13 +884,19 @@ describe('utils', () => { { message: { type: 'Literal', value: 'message1' }, messageId: { type: 'Literal', value: 'messageId1' }, - data: { type: 'ObjectExpression', properties: [{ key: { name: 'foo' } }] }, + data: { + type: 'ObjectExpression', + properties: [{ key: { name: 'foo' } }], + }, fix: { type: 'FunctionExpression' }, }, { message: { type: 'Literal', value: 'message2' }, messageId: { type: 'Literal', value: 'messageId2' }, - data: { type: 'ObjectExpression', properties: [{ key: { name: 'bar' } }] }, + data: { + type: 'ObjectExpression', + properties: [{ key: { name: 'bar' } }], + }, fix: { type: 'FunctionExpression' }, }, ], @@ -740,7 +917,10 @@ describe('utils', () => { { message: { type: 'Literal', value: 'message1' }, messageId: { type: 'Literal', value: 'messageId1' }, - data: { type: 'ObjectExpression', properties: [{ key: { name: 'foo' } }] }, + data: { + type: 'ObjectExpression', + properties: [{ key: { name: 'foo' } }], + }, fix: { type: 'FunctionExpression' }, }, ], @@ -761,7 +941,10 @@ describe('utils', () => { { message: { type: 'Literal', value: 'message1' }, messageId: { type: 'Literal', value: 'messageId1' }, - data: { type: 'ObjectExpression', properties: [{ key: { name: 'foo' } }] }, + data: { + type: 'ObjectExpression', + properties: [{ key: { name: 'foo' } }], + }, fix: { type: 'FunctionExpression' }, }, ], @@ -781,7 +964,10 @@ describe('utils', () => { { message: { type: 'Literal', value: 'message1' }, messageId: { type: 'Literal', value: 'messageId1' }, - data: { type: 'ObjectExpression', properties: [{ key: { name: 'foo' } }] }, + data: { + type: 'ObjectExpression', + properties: [{ key: { name: 'foo' } }], + }, fix: { type: 'FunctionExpression' }, }, ], @@ -790,14 +976,19 @@ describe('utils', () => { it('behaves correctly', () => { for (const testCase of CASES) { - const ast = espree.parse(testCase.code, { ecmaVersion: 6, range: true }); - const context = { getScope () {} }; // mock object + const ast = espree.parse(testCase.code, { + ecmaVersion: 6, + range: true, + }); + const context = { getScope() {} }; // mock object const reportNode = ast.body[0].expression; const reportInfo = utils.getReportInfo(reportNode.arguments, context); const data = utils.collectReportViolationAndSuggestionData(reportInfo); assert( lodash.isMatch(data, testCase.shouldMatch), - `Expected \n${inspect(data)}\nto match\n${inspect(testCase.shouldMatch)}` + `Expected \n${inspect(data)}\nto match\n${inspect( + testCase.shouldMatch + )}` ); } }); @@ -806,44 +997,93 @@ describe('utils', () => { describe('isAutoFixerFunction / isSuggestionFixerFunction', () => { const CASES = { // isAutoFixerFunction - 'context.report({ fix(fixer) {} });' (ast) { - return { expected: true, node: ast.body[0].expression.arguments[0].properties[0].value, context: ast.body[0].expression.callee.object, fn: utils.isAutoFixerFunction }; + 'context.report({ fix(fixer) {} });'(ast) { + return { + expected: true, + node: ast.body[0].expression.arguments[0].properties[0].value, + context: ast.body[0].expression.callee.object, + fn: utils.isAutoFixerFunction, + }; }, - 'context.notReport({ fix(fixer) {} });' (ast) { - return { expected: false, node: ast.body[0].expression.arguments[0].properties[0].value, context: ast.body[0].expression.callee.object, fn: utils.isAutoFixerFunction }; + 'context.notReport({ fix(fixer) {} });'(ast) { + return { + expected: false, + node: ast.body[0].expression.arguments[0].properties[0].value, + context: ast.body[0].expression.callee.object, + fn: utils.isAutoFixerFunction, + }; }, - 'context.report({ notFix(fixer) {} });' (ast) { - return { expected: false, node: ast.body[0].expression.arguments[0].properties[0].value, context: ast.body[0].expression.callee.object, fn: utils.isAutoFixerFunction }; + 'context.report({ notFix(fixer) {} });'(ast) { + return { + expected: false, + node: ast.body[0].expression.arguments[0].properties[0].value, + context: ast.body[0].expression.callee.object, + fn: utils.isAutoFixerFunction, + }; }, - 'notContext.report({ notFix(fixer) {} });' (ast) { - return { expected: false, node: ast.body[0].expression.arguments[0].properties[0].value, context: undefined, fn: utils.isAutoFixerFunction }; + 'notContext.report({ notFix(fixer) {} });'(ast) { + return { + expected: false, + node: ast.body[0].expression.arguments[0].properties[0].value, + context: undefined, + fn: utils.isAutoFixerFunction, + }; }, // isSuggestionFixerFunction - 'context.report({ suggest: [{ fix(fixer) {} }] });' (ast) { - return { expected: true, node: ast.body[0].expression.arguments[0].properties[0].value.elements[0].properties[0].value, context: ast.body[0].expression.callee.object, fn: utils.isSuggestionFixerFunction }; + 'context.report({ suggest: [{ fix(fixer) {} }] });'(ast) { + return { + expected: true, + node: ast.body[0].expression.arguments[0].properties[0].value + .elements[0].properties[0].value, + context: ast.body[0].expression.callee.object, + fn: utils.isSuggestionFixerFunction, + }; }, - 'context.notReport({ suggest: [{ fix(fixer) {} }] });' (ast) { - return { expected: false, node: ast.body[0].expression.arguments[0].properties[0].value.elements[0].properties[0].value, context: ast.body[0].expression.callee.object, fn: utils.isSuggestionFixerFunction }; + 'context.notReport({ suggest: [{ fix(fixer) {} }] });'(ast) { + return { + expected: false, + node: ast.body[0].expression.arguments[0].properties[0].value + .elements[0].properties[0].value, + context: ast.body[0].expression.callee.object, + fn: utils.isSuggestionFixerFunction, + }; }, - 'context.report({ notSuggest: [{ fix(fixer) {} }] });' (ast) { - return { expected: false, node: ast.body[0].expression.arguments[0].properties[0].value.elements[0].properties[0].value, context: ast.body[0].expression.callee.object, fn: utils.isSuggestionFixerFunction }; + 'context.report({ notSuggest: [{ fix(fixer) {} }] });'(ast) { + return { + expected: false, + node: ast.body[0].expression.arguments[0].properties[0].value + .elements[0].properties[0].value, + context: ast.body[0].expression.callee.object, + fn: utils.isSuggestionFixerFunction, + }; }, - 'context.report({ suggest: [{ notFix(fixer) {} }] });' (ast) { - return { expected: false, node: ast.body[0].expression.arguments[0].properties[0].value.elements[0].properties[0].value, context: ast.body[0].expression.callee.object, fn: utils.isSuggestionFixerFunction }; + 'context.report({ suggest: [{ notFix(fixer) {} }] });'(ast) { + return { + expected: false, + node: ast.body[0].expression.arguments[0].properties[0].value + .elements[0].properties[0].value, + context: ast.body[0].expression.callee.object, + fn: utils.isSuggestionFixerFunction, + }; }, - 'notContext.report({ suggest: [{ fix(fixer) {} }] });' (ast) { - return { expected: false, node: ast.body[0].expression.arguments[0].properties[0].value, context: undefined, fn: utils.isSuggestionFixerFunction }; + 'notContext.report({ suggest: [{ fix(fixer) {} }] });'(ast) { + return { + expected: false, + node: ast.body[0].expression.arguments[0].properties[0].value, + context: undefined, + fn: utils.isSuggestionFixerFunction, + }; }, }; - Object.keys(CASES).forEach(ruleSource => { + Object.keys(CASES).forEach((ruleSource) => { it(ruleSource, () => { const ast = espree.parse(ruleSource, { ecmaVersion: 6, range: true }); // Add parent to each node. estraverse.traverse(ast, { - enter (node, parent) { + enter(node, parent) { node.parent = parent; }, });