feat: new if-newline rule

This commit is contained in:
Anthony Fu 2022-04-02 23:51:24 +08:00
parent a6692cc074
commit 2e9948029c
7 changed files with 277 additions and 1 deletions

View File

@ -292,5 +292,6 @@ module.exports = {
// antfu
'antfu/no-leading-newline': 'error',
'antfu/if-newline': 'error',
},
}

View File

@ -18,12 +18,14 @@
"scripts": {
"build": "rimraf dist && unbuild",
"stub": "unbuild --stub",
"test": "vitest",
"prepublishOnly": "nr build"
},
"dependencies": {
"@typescript-eslint/utils": "^5.17.0"
},
"devDependencies": {
"unbuild": "^0.7.0"
"unbuild": "^0.7.0",
"vitest": "^0.8.2"
}
}

View File

@ -1,7 +1,9 @@
import ifNewline from './rules/if-newline'
import noLeadingNewline from './rules/no-leading-newline'
export default {
rules: {
'no-leading-newline': noLeadingNewline,
'if-newline': ifNewline,
},
}

View File

@ -0,0 +1,30 @@
import { RuleTester } from '@typescript-eslint/utils/dist/ts-eslint'
import { it } from 'vitest'
import rule, { RULE_NAME } from './if-newline'
const valids = [
`if (true)
console.log('hello')
`,
`if (true) {
console.log('hello')
}`,
]
const invalids = [
['if (true) console.log(\'hello\')', 'if (true) \nconsole.log(\'hello\')'],
]
it('runs', () => {
const ruleTester: RuleTester = new RuleTester({
parser: require.resolve('@typescript-eslint/parser'),
})
ruleTester.run(RULE_NAME, rule, {
valid: valids,
invalid: invalids.map(i => ({
code: i[0],
output: i[1],
errors: [{ messageId: 'missingIfNewline' }],
})),
})
})

View File

@ -0,0 +1,45 @@
import { createEslintRule } from '../utils'
export const RULE_NAME = 'if-newline'
export type MessageIds = 'missingIfNewline'
export type Options = []
export default createEslintRule<Options, MessageIds>({
name: RULE_NAME,
meta: {
type: 'problem',
docs: {
description: 'Newline after if',
recommended: 'error',
},
fixable: 'code',
schema: [],
messages: {
missingIfNewline: 'Expect newline after if',
},
},
defaultOptions: [],
create: (context) => {
return {
IfStatement(node) {
if (!node.consequent)
return
if (node.consequent.type === 'BlockStatement')
return
if (node.test.loc.end.line === node.consequent.loc.start.line) {
context.report({
node,
loc: {
start: node.test.loc.end,
end: node.consequent.loc.start,
},
messageId: 'missingIfNewline',
fix(fixer) {
return fixer.replaceTextRange([node.consequent.range[0], node.consequent.range[0]], '\n')
},
})
}
},
}
},
})

View File

@ -0,0 +1,27 @@
import { RuleTester } from '@typescript-eslint/utils/dist/ts-eslint'
import { it } from 'vitest'
import rule, { RULE_NAME } from './no-leading-newline'
const valids = [
'import {} from \'foo\'',
`// comment
import {} from ''`,
]
const invalids = [
'\n\nimport {} from \'fo\'',
]
it('runs', () => {
const ruleTester: RuleTester = new RuleTester({
parser: require.resolve('@typescript-eslint/parser'),
})
ruleTester.run(RULE_NAME, rule, {
valid: valids,
invalid: invalids.map(i => ({
code: i,
output: i.trim(),
errors: [{ messageId: 'noLeadingNewline' }],
})),
})
})

View File

@ -88,10 +88,12 @@ importers:
specifiers:
'@typescript-eslint/utils': ^5.17.0
unbuild: ^0.7.0
vitest: ^0.8.2
dependencies:
'@typescript-eslint/utils': 5.17.0_eslint@8.12.0+typescript@4.6.3
devDependencies:
unbuild: 0.7.2
vitest: 0.8.2
packages/react:
specifiers:
@ -510,6 +512,16 @@ packages:
picomatch: 2.2.2
dev: true
/@types/chai-subset/1.3.3:
resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==}
dependencies:
'@types/chai': 4.3.0
dev: true
/@types/chai/4.3.0:
resolution: {integrity: sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==}
dev: true
/@types/color-name/1.1.1:
resolution: {integrity: sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==}
dev: true
@ -762,6 +774,10 @@ packages:
es-abstract: 1.19.1
dev: false
/assertion-error/1.1.0:
resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
dev: true
/balanced-match/1.0.0:
resolution: {integrity: sha1-ibTRmasr7kneFk6gK4nORi1xt2c=}
@ -832,6 +848,19 @@ packages:
resolution: {integrity: sha512-/eYp1J6zYh1alySQB4uzYFkLmxxI8tk0kxldbNHXp8+v+rdMKdUBNjRLz7T7fz6Iox+1lIdYpc7rq6ZcXfTukg==}
dev: true
/chai/4.3.6:
resolution: {integrity: sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==}
engines: {node: '>=4'}
dependencies:
assertion-error: 1.1.0
check-error: 1.0.2
deep-eql: 3.0.1
get-func-name: 2.0.0
loupe: 2.3.4
pathval: 1.1.1
type-detect: 4.0.8
dev: true
/chalk/2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
@ -865,6 +894,10 @@ packages:
resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==}
dev: false
/check-error/1.0.2:
resolution: {integrity: sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=}
dev: true
/ci-info/3.3.0:
resolution: {integrity: sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==}
dev: false
@ -966,6 +999,13 @@ packages:
dependencies:
ms: 2.1.2
/deep-eql/3.0.1:
resolution: {integrity: sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==}
engines: {node: '>=0.12'}
dependencies:
type-detect: 4.0.8
dev: true
/deep-is/0.1.3:
resolution: {integrity: sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=}
dev: true
@ -1938,6 +1978,10 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
/get-func-name/2.0.0:
resolution: {integrity: sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=}
dev: true
/get-intrinsic/1.1.1:
resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==}
dependencies:
@ -2143,6 +2187,12 @@ packages:
dependencies:
has: 1.0.3
/is-core-module/2.8.1:
resolution: {integrity: sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==}
dependencies:
has: 1.0.3
dev: true
/is-date-object/1.0.2:
resolution: {integrity: sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==}
engines: {node: '>= 0.4'}
@ -2324,6 +2374,11 @@ packages:
resolution: {integrity: sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=}
dev: false
/local-pkg/0.4.1:
resolution: {integrity: sha512-lL87ytIGP2FU5PWwNDo0w3WhIo2gopIAxPg9RxDYF7m4rr5ahuZxP22xnJHIvaLTe4Z9P6uKKY2UHiwyB4pcrw==}
engines: {node: '>=14'}
dev: true
/locate-path/2.0.0:
resolution: {integrity: sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=}
engines: {node: '>=4'}
@ -2357,6 +2412,12 @@ packages:
dependencies:
js-tokens: 4.0.0
/loupe/2.3.4:
resolution: {integrity: sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==}
dependencies:
get-func-name: 2.0.0
dev: true
/lru-cache/6.0.0:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'}
@ -2477,6 +2538,12 @@ packages:
/ms/2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
/nanoid/3.3.2:
resolution: {integrity: sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
dev: true
/natural-compare/1.4.0:
resolution: {integrity: sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=}
@ -2658,6 +2725,10 @@ packages:
/path-parse/1.0.6:
resolution: {integrity: sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==}
/path-parse/1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
dev: true
/path-type/4.0.0:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
@ -2666,6 +2737,10 @@ packages:
resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==}
dev: true
/pathval/1.1.1:
resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
dev: true
/picocolors/1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
dev: true
@ -2687,6 +2762,15 @@ packages:
engines: {node: '>=4'}
dev: false
/postcss/8.4.12:
resolution: {integrity: sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.2
picocolors: 1.0.0
source-map-js: 1.0.2
dev: true
/prelude-ls/1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
@ -2776,6 +2860,15 @@ packages:
is-core-module: 2.8.0
path-parse: 1.0.6
/resolve/1.22.0:
resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==}
hasBin: true
dependencies:
is-core-module: 2.8.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
dev: true
/resolve/2.0.0-next.3:
resolution: {integrity: sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==}
dependencies:
@ -2895,6 +2988,11 @@ packages:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}
/source-map-js/1.0.2:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'}
dev: true
/source-map/0.5.7:
resolution: {integrity: sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=}
engines: {node: '>=0.10.0'}
@ -2995,10 +3093,25 @@ packages:
has-flag: 4.0.0
dev: true
/supports-preserve-symlinks-flag/1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
dev: true
/text-table/0.2.0:
resolution: {integrity: sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=}
dev: true
/tinypool/0.1.2:
resolution: {integrity: sha512-fvtYGXoui2RpeMILfkvGIgOVkzJEGediv8UJt7TxdAOY8pnvUkFg/fkvqTfXG9Acc9S17Cnn1S4osDc2164guA==}
engines: {node: '>=14.0.0'}
dev: true
/tinyspy/0.3.0:
resolution: {integrity: sha512-c5uFHqtUp74R2DJE3/Efg0mH5xicmgziaQXMm/LvuuZn3RdpADH32aEGDRyCzObXT1DNfwDMqRQ/Drh1MlO12g==}
engines: {node: '>=14.0.0'}
dev: true
/to-fast-properties/2.0.0:
resolution: {integrity: sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=}
engines: {node: '>=4'}
@ -3154,6 +3267,62 @@ packages:
spdx-expression-parse: 3.0.1
dev: false
/vite/2.9.1:
resolution: {integrity: sha512-vSlsSdOYGcYEJfkQ/NeLXgnRv5zZfpAsdztkIrs7AZHV8RCMZQkwjo4DS5BnrYTqoWqLoUe1Cah4aVO4oNNqCQ==}
engines: {node: '>=12.2.0'}
hasBin: true
peerDependencies:
less: '*'
sass: '*'
stylus: '*'
peerDependenciesMeta:
less:
optional: true
sass:
optional: true
stylus:
optional: true
dependencies:
esbuild: 0.14.30
postcss: 8.4.12
resolve: 1.22.0
rollup: 2.70.1
optionalDependencies:
fsevents: 2.3.2
dev: true
/vitest/0.8.2:
resolution: {integrity: sha512-dTFDJl2F3pkWy1tcE3M29LasklhgtP7M88kT7AJcAurX7nCl/eWu1PQeSzjzWQyUbDq2p8jqdoLETd7MDeibcA==}
engines: {node: '>=v14.16.0'}
hasBin: true
peerDependencies:
'@vitest/ui': '*'
c8: '*'
happy-dom: '*'
jsdom: '*'
peerDependenciesMeta:
'@vitest/ui':
optional: true
c8:
optional: true
happy-dom:
optional: true
jsdom:
optional: true
dependencies:
'@types/chai': 4.3.0
'@types/chai-subset': 1.3.3
chai: 4.3.6
local-pkg: 0.4.1
tinypool: 0.1.2
tinyspy: 0.3.0
vite: 2.9.1
transitivePeerDependencies:
- less
- sass
- stylus
dev: true
/vue-eslint-parser/8.0.1_eslint@8.12.0:
resolution: {integrity: sha512-lhWjDXJhe3UZw2uu3ztX51SJAPGPey1Tff2RK3TyZURwbuI4vximQLzz4nQfCv8CZq4xx7uIiogHMMoSJPr33A==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}