3b2732bc36
* add no-typos rule to eslint * return early when function name is correct * Handle null decl.type * update check Co-authored-by: JJ Kasper <jj@jjsweb.site>
107 lines
2.4 KiB
JavaScript
107 lines
2.4 KiB
JavaScript
const path = require('path')
|
|
|
|
const NEXT_EXPORT_FUNCTIONS = [
|
|
'getStaticProps',
|
|
'getStaticPaths',
|
|
'getServerSideProps',
|
|
]
|
|
|
|
// 0 is the exact match
|
|
const THRESHOLD = 1
|
|
|
|
// the minimum number of operations required to convert string a to string b.
|
|
function minDistance(a, b) {
|
|
const m = a.length
|
|
const n = b.length
|
|
|
|
if (m < n) {
|
|
return minDistance(b, a)
|
|
}
|
|
|
|
if (n === 0) {
|
|
return m
|
|
}
|
|
|
|
let previousRow = Array.from({ length: n + 1 }, (_, i) => i)
|
|
|
|
for (let i = 0; i < m; i++) {
|
|
const s1 = a[i]
|
|
let currentRow = [i + 1]
|
|
for (let j = 0; j < n; j++) {
|
|
const s2 = b[j]
|
|
const insertions = previousRow[j + 1] + 1
|
|
const deletions = currentRow[j] + 1
|
|
const substitutions = previousRow[j] + Number(s1 !== s2)
|
|
currentRow.push(Math.min(insertions, deletions, substitutions))
|
|
}
|
|
previousRow = currentRow
|
|
}
|
|
return previousRow[previousRow.length - 1]
|
|
}
|
|
|
|
module.exports = {
|
|
meta: {
|
|
docs: {
|
|
description: 'Prevent common typos',
|
|
category: 'Stylistic Issues',
|
|
recommended: true,
|
|
},
|
|
fixable: null,
|
|
},
|
|
|
|
create: function (context) {
|
|
function checkTypos(node, name) {
|
|
if (NEXT_EXPORT_FUNCTIONS.includes(name)) {
|
|
return
|
|
}
|
|
|
|
const potentialTypos = NEXT_EXPORT_FUNCTIONS.map((o) => ({
|
|
option: o,
|
|
distance: minDistance(o, name),
|
|
}))
|
|
.filter(({ distance }) => distance <= THRESHOLD && distance > 0)
|
|
.sort((a, b) => a.distance - b.distance)
|
|
|
|
if (potentialTypos.length) {
|
|
context.report({
|
|
node,
|
|
message: `${name} may be a typo. Did you mean ${potentialTypos[0].option}?`,
|
|
})
|
|
}
|
|
}
|
|
return {
|
|
ExportNamedDeclaration(node) {
|
|
const page = context.getFilename().split('pages')[1]
|
|
if (!page || path.parse(page).dir.startsWith('/api')) {
|
|
return
|
|
}
|
|
|
|
const decl = node.declaration
|
|
|
|
if (!decl) {
|
|
return
|
|
}
|
|
|
|
switch (decl.type) {
|
|
case 'FunctionDeclaration': {
|
|
checkTypos(node, decl.id.name)
|
|
break
|
|
}
|
|
case 'VariableDeclaration': {
|
|
decl.declarations.forEach((d) => {
|
|
if (d.id.type !== 'Identifier') {
|
|
return
|
|
}
|
|
checkTypos(node, d.id.name)
|
|
})
|
|
break
|
|
}
|
|
default: {
|
|
break
|
|
}
|
|
}
|
|
return
|
|
},
|
|
}
|
|
},
|
|
}
|