From a99a89dafc246092c4f49a748540e8a1eb365867 Mon Sep 17 00:00:00 2001 From: fisker Date: Fri, 11 Dec 2020 01:40:48 +0800 Subject: [PATCH] Add rule `empty-brace-spaces` --- docs/rules/empty-brace-spaces.md | 28 ++++++++ index.js | 1 + readme.md | 2 + rules/empty-brace-spaces.js | 54 +++++++++++++++ test/empty-brace-spaces.js | 84 +++++++++++++++++++++++ test/snapshots/empty-brace-space.js.md | 33 +++++++++ test/snapshots/empty-brace-space.js.snap | Bin 0 -> 232 bytes 7 files changed, 202 insertions(+) create mode 100644 docs/rules/empty-brace-spaces.md create mode 100644 rules/empty-brace-spaces.js create mode 100644 test/empty-brace-spaces.js create mode 100644 test/snapshots/empty-brace-space.js.md create mode 100644 test/snapshots/empty-brace-space.js.snap diff --git a/docs/rules/empty-brace-spaces.md b/docs/rules/empty-brace-spaces.md new file mode 100644 index 0000000000..8f02f2de95 --- /dev/null +++ b/docs/rules/empty-brace-spaces.md @@ -0,0 +1,28 @@ +# Enforce no spaces between braces + +This rule is fixable. + +## Fail + +```js +class Unicorn { +} +``` + +```js +try { + foo(); +} catch { } +``` + +## Pass + +```js +class Unicorn {} +``` + +```js +try { + foo(); +} catch {} +``` diff --git a/index.js b/index.js index 6b0f38d5ec..0268ea0cfa 100644 --- a/index.js +++ b/index.js @@ -21,6 +21,7 @@ module.exports = { 'unicorn/catch-error-name': 'error', 'unicorn/consistent-function-scoping': 'error', 'unicorn/custom-error-definition': 'off', + 'unicorn/empty-brace-space': 'error', 'unicorn/error-message': 'error', 'unicorn/escape-case': 'error', 'unicorn/expiring-todo-comments': 'error', diff --git a/readme.md b/readme.md index ee4b1b5a29..aaa2990263 100644 --- a/readme.md +++ b/readme.md @@ -37,6 +37,7 @@ Configure it in `package.json`. "unicorn/catch-error-name": "error", "unicorn/consistent-function-scoping": "error", "unicorn/custom-error-definition": "off", + "unicorn/empty-brace-spaces": "error", "unicorn/error-message": "error", "unicorn/escape-case": "error", "unicorn/expiring-todo-comments": "error", @@ -104,6 +105,7 @@ Configure it in `package.json`. - [catch-error-name](docs/rules/catch-error-name.md) - Enforce a specific parameter name in catch clauses. *(fixable)* - [consistent-function-scoping](docs/rules/consistent-function-scoping.md) - Move function definitions to the highest possible scope. - [custom-error-definition](docs/rules/custom-error-definition.md) - Enforce correct `Error` subclassing. *(fixable)* +- [empty-brace-spaces](docs/rules/empty-brace-spaces.md) - Enforce no spaces between braces. *(fixable)* - [error-message](docs/rules/error-message.md) - Enforce passing a `message` value when creating a built-in error. - [escape-case](docs/rules/escape-case.md) - Require escape sequences to use uppercase values. *(fixable)* - [expiring-todo-comments](docs/rules/expiring-todo-comments.md) - Add expiration conditions to TODO comments. diff --git a/rules/empty-brace-spaces.js b/rules/empty-brace-spaces.js new file mode 100644 index 0000000000..ba379504e2 --- /dev/null +++ b/rules/empty-brace-spaces.js @@ -0,0 +1,54 @@ +'use strict'; +const getDocumentationUrl = require('./utils/get-documentation-url'); + +const MESSAGE_ID = 'empty-brace-spaces'; +const messages = { + [MESSAGE_ID]: 'Do not add spaces between braces.' +}; + +const selector = `:matches(${ + [ + 'BlockStatement[body.length=0]', + 'ClassBody[body.length=0]', + 'ObjectExpression[properties.length=0]' + ].join(', ') +})`; + +const create = context => { + const sourceCode = context.getSourceCode(); + return { + [selector](node) { + let [start, end] = node.range; + start = start + 1; + end = end - 1; + + if (!/^\s+$/.test(sourceCode.text.slice(start, end))) { + return; + } + + context.report({ + loc: { + start: sourceCode.getLocFromIndex(start), + end: sourceCode.getLocFromIndex(end) + }, + messageId: MESSAGE_ID, + fix: fixer => fixer.replaceTextRange([start, end], '') + }); + } + } +}; + +const schema = []; + +module.exports = { + create, + meta: { + type: 'layout', + docs: { + url: getDocumentationUrl(__filename) + }, + fixable: 'whitespace', + schema, + messages + } +}; diff --git a/test/empty-brace-spaces.js b/test/empty-brace-spaces.js new file mode 100644 index 0000000000..10b34c0c2a --- /dev/null +++ b/test/empty-brace-spaces.js @@ -0,0 +1,84 @@ +import {outdent} from 'outdent'; +import {test} from './utils/test'; + +const SPACES_PLACEHOLDER = '/* */'; +const cases = [ + '{/* */}', + 'function foo(){/* */}', + 'if(foo) {/* */}', + 'if(foo) {} else if (bar) {/* */}', + 'if(foo) {} else {/* */}', + 'for(;;){/* */}', + 'for(foo in bar){/* */}', + 'for(foo of bar){/* */}', + 'switch (foo) {case bar: {/* */}}', + 'switch (foo) {default: {/* */}}', + 'try {/* */} catch(foo){}', + 'try {} catch(foo){/* */}', + 'try {} catch(foo){} finally {/* */}', + 'do {/* */} while (foo)', + 'while (foo){/* */}', + 'foo = () => {/* */}', + 'foo = function (){/* */}', + 'function foo(){/* */}', + 'foo = {/* */}', + 'class Foo {bar() {/* */}}', + 'foo = class {bar() {/* */}}' +]; +const classBodyCases = [ + 'class Foo {/* */}', + 'foo = class {/* */}', +]; +const allCases = [...cases, ...classBodyCases]; + +const casesIgnored = [ + 'switch (foo) {/* */}', + 'const {/* */} = foo', + 'import {/* */} from "foo"' +]; + +test({ + valid: [ + // We don't check these cases + ...casesIgnored.map(code => code.replace(SPACES_PLACEHOLDER, ' ')), + // No space + ...allCases.map(code => code.replace(SPACES_PLACEHOLDER, '')), + // Comments + ...allCases.map(code => code.replace(SPACES_PLACEHOLDER, '/* comment */')), + ...allCases.map(code => code.replace(SPACES_PLACEHOLDER, '\n\t// comment \n')), + // Not empty + ...cases.map(code => code.replace(SPACES_PLACEHOLDER, 'unicorn')), + ...classBodyCases.map(code => code.replace(SPACES_PLACEHOLDER, 'bar() {}')), + // `with` + { + code: 'with (foo) {}', + parserOptions: {ecmaVersion: 5, sourceType: 'script'} + } + ], + invalid: [ + ...[' ', '\t', ' \t \t ', '\n\n', '\r\n'].map(space => + allCases.map(code => ({ + code: code.replace(SPACES_PLACEHOLDER, space), + output: code.replace(SPACES_PLACEHOLDER, ''), + errors: 1 + })) + ).flat(), + // `with` + { + code: 'with (foo) { }', + output: 'with (foo) {}', + errors: 1, + parserOptions: {ecmaVersion: 5, sourceType: 'script'} + } + ] +}); + +test.visualize([ + outdent` + try { + foo(); + } catch (error) { + \u0020\u0020\u0020\u0020\u0020\u0020\u0020 + } + ` +]); diff --git a/test/snapshots/empty-brace-space.js.md b/test/snapshots/empty-brace-space.js.md new file mode 100644 index 0000000000..2eff5d8df4 --- /dev/null +++ b/test/snapshots/empty-brace-space.js.md @@ -0,0 +1,33 @@ +# Snapshot report for `test/empty-brace-space.js` + +The actual snapshot is saved in `empty-brace-space.js.snap`. + +Generated by [AVA](https://avajs.dev). + +## empty-brace-space - #1 + +> Snapshot 1 + + `␊ + Input:␊ + 1 | try {␊ + 2 | foo();␊ + 3 | } catch (error) {␊ + 4 | ␊ + 5 | }␊ + ␊ + Output:␊ + 1 | try {␊ + 2 | foo();␊ + 3 | } catch (error) {}␊ + ␊ + Error 1/1:␊ + 1 | try {␊ + 2 | foo();␊ + > 3 | } catch (error) {␊ + | ^␊ + > 4 | ␊ + | ^^^^^^^^␊ + > 5 | }␊ + | ^ Do not add spaces between braces.␊ + ` diff --git a/test/snapshots/empty-brace-space.js.snap b/test/snapshots/empty-brace-space.js.snap new file mode 100644 index 0000000000000000000000000000000000000000..c552fb7a92f13caea99a532ee719e5136ab6695b GIT binary patch literal 232 zcmV?0O86S%X00000000A1 zU|?WiWLUE5Y4+95l@(9dYv{YGs$XSf00Czp#mvACW;3!f2r`*4a(U(zl$Ka=DJU2! z)F_k`RVq{i*+xJ%XIg%~hNd-;XAI=kDkLYCBxfjSq!tzB7iof3nSfL&Kmd?y3R1?! z