eslint-plugin-unicorn
Version:
Various awesome ESLint rules
127 lines (111 loc) • 2.93 kB
JavaScript
const getDocumentationUrl = require('./utils/get-documentation-url');
const methodSelector = require('./utils/method-selector');
const ERROR_MESSAGE_ID = 'error';
const SUGGESTION_REPLACE_MESSAGE_ID = 'replace';
const SUGGESTION_REMOVE_MESSAGE_ID = 'remove';
const messages = {
[ERROR_MESSAGE_ID]: 'Use `undefined` instead of `null`.',
[SUGGESTION_REPLACE_MESSAGE_ID]: 'Replace `null` with `undefined`.',
[SUGGESTION_REMOVE_MESSAGE_ID]: 'Remove `null`.'
};
const objectCreateSelector = methodSelector({
object: 'Object',
name: 'create',
length: 1
});
// `useRef(null)`
// eslint-disable-next-line unicorn/prevent-abbreviations
const useRefSelector = [
'CallExpression',
'[callee.type="Identifier"]',
'[callee.name="useRef"]',
'[arguments.length=1]',
'[arguments.0.type!="SpreadElement"]'
].join('');
// `React.useRef(null)`
// eslint-disable-next-line unicorn/prevent-abbreviations
const reactUseRefSelector = methodSelector({
object: 'React',
name: 'useRef',
length: 1
});
const selector = [
'Literal',
'[raw="null"]',
`:not(:matches(${[objectCreateSelector, useRefSelector, reactUseRefSelector].join(', ')}) > .arguments)`
].join('');
const isLooseEqual = node => node.type === 'BinaryExpression' && ['==', '!='].includes(node.operator);
const isStrictEqual = node => node.type === 'BinaryExpression' && ['===', '!=='].includes(node.operator);
const create = context => {
const {checkStrictEquality} = {
checkStrictEquality: false,
...context.options[0]
};
return {
[selector]: node => {
const problem = {
node,
messageId: ERROR_MESSAGE_ID
};
/* istanbul ignore next */
const {parent = {}, range} = node;
if (!checkStrictEquality && isStrictEqual(parent)) {
return;
}
const fix = fixer => fixer.replaceText(node, 'undefined');
const replaceSuggestion = {
messageId: SUGGESTION_REPLACE_MESSAGE_ID,
fix
};
if (isLooseEqual(parent)) {
problem.fix = fix;
} else if (parent.type === 'ReturnStatement' && parent.argument === node) {
problem.suggest = [
{
messageId: SUGGESTION_REMOVE_MESSAGE_ID,
fix: fixer => fixer.remove(node)
},
replaceSuggestion
];
} else if (parent.type === 'VariableDeclarator' && parent.init === node && parent.parent.kind !== 'const') {
problem.suggest = [
{
messageId: SUGGESTION_REMOVE_MESSAGE_ID,
fix: fixer => fixer.removeRange([parent.id.range[1], range[1]])
},
replaceSuggestion
];
} else {
problem.suggest = [
replaceSuggestion
];
}
context.report(problem);
}
};
};
const schema = [
{
type: 'object',
properties: {
checkStrictEquality: {
type: 'boolean',
default: false
}
},
additionalProperties: false
}
];
module.exports = {
create,
meta: {
type: 'suggestion',
docs: {
url: getDocumentationUrl(__filename)
},
messages,
schema,
fixable: 'code'
}
};
;