You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
137 lines
3.9 KiB
137 lines
3.9 KiB
/** |
|
* @fileoverview Enforce boolean attributes notation in JSX |
|
* @author Yannick Croissant |
|
*/ |
|
|
|
'use strict'; |
|
|
|
const docsUrl = require('../util/docsUrl'); |
|
const report = require('../util/report'); |
|
|
|
// ------------------------------------------------------------------------------ |
|
// Rule Definition |
|
// ------------------------------------------------------------------------------ |
|
|
|
const exceptionsSchema = { |
|
type: 'array', |
|
items: { type: 'string', minLength: 1 }, |
|
uniqueItems: true, |
|
}; |
|
|
|
const ALWAYS = 'always'; |
|
const NEVER = 'never'; |
|
|
|
const errorData = new WeakMap(); |
|
function getErrorData(exceptions) { |
|
if (!errorData.has(exceptions)) { |
|
const exceptionProps = Array.from(exceptions, (name) => `\`${name}\``).join(', '); |
|
const exceptionsMessage = exceptions.size > 0 ? ` for the following props: ${exceptionProps}` : ''; |
|
errorData.set(exceptions, { exceptionsMessage }); |
|
} |
|
return errorData.get(exceptions); |
|
} |
|
|
|
function isAlways(configuration, exceptions, propName) { |
|
const isException = exceptions.has(propName); |
|
if (configuration === ALWAYS) { |
|
return !isException; |
|
} |
|
return isException; |
|
} |
|
|
|
function isNever(configuration, exceptions, propName) { |
|
const isException = exceptions.has(propName); |
|
if (configuration === NEVER) { |
|
return !isException; |
|
} |
|
return isException; |
|
} |
|
|
|
const messages = { |
|
omitBoolean: 'Value must be omitted for boolean attributes{{exceptionsMessage}}', |
|
omitBoolean_noMessage: 'Value must be omitted for boolean attributes', |
|
setBoolean: 'Value must be set for boolean attributes{{exceptionsMessage}}', |
|
setBoolean_noMessage: 'Value must be set for boolean attributes', |
|
}; |
|
|
|
module.exports = { |
|
meta: { |
|
docs: { |
|
description: 'Enforce boolean attributes notation in JSX', |
|
category: 'Stylistic Issues', |
|
recommended: false, |
|
url: docsUrl('jsx-boolean-value'), |
|
}, |
|
fixable: 'code', |
|
|
|
messages, |
|
|
|
schema: { |
|
anyOf: [{ |
|
type: 'array', |
|
items: [{ enum: [ALWAYS, NEVER] }], |
|
additionalItems: false, |
|
}, { |
|
type: 'array', |
|
items: [{ |
|
enum: [ALWAYS], |
|
}, { |
|
type: 'object', |
|
additionalProperties: false, |
|
properties: { |
|
[NEVER]: exceptionsSchema, |
|
}, |
|
}], |
|
additionalItems: false, |
|
}, { |
|
type: 'array', |
|
items: [{ |
|
enum: [NEVER], |
|
}, { |
|
type: 'object', |
|
additionalProperties: false, |
|
properties: { |
|
[ALWAYS]: exceptionsSchema, |
|
}, |
|
}], |
|
additionalItems: false, |
|
}], |
|
}, |
|
}, |
|
|
|
create(context) { |
|
const configuration = context.options[0] || NEVER; |
|
const configObject = context.options[1] || {}; |
|
const exceptions = new Set((configuration === ALWAYS ? configObject[NEVER] : configObject[ALWAYS]) || []); |
|
|
|
return { |
|
JSXAttribute(node) { |
|
const propName = node.name && node.name.name; |
|
const value = node.value; |
|
|
|
if (isAlways(configuration, exceptions, propName) && value === null) { |
|
const data = getErrorData(exceptions); |
|
const messageId = data.exceptionsMessage ? 'setBoolean' : 'setBoolean_noMessage'; |
|
report(context, messages[messageId], messageId, { |
|
node, |
|
data, |
|
fix(fixer) { |
|
return fixer.insertTextAfter(node, '={true}'); |
|
}, |
|
}); |
|
} |
|
if (isNever(configuration, exceptions, propName) && value && value.type === 'JSXExpressionContainer' && value.expression.value === true) { |
|
const data = getErrorData(exceptions); |
|
const messageId = data.exceptionsMessage ? 'omitBoolean' : 'omitBoolean_noMessage'; |
|
report(context, messages[messageId], messageId, { |
|
node, |
|
data, |
|
fix(fixer) { |
|
return fixer.removeRange([node.name.range[1], value.range[1]]); |
|
}, |
|
}); |
|
} |
|
}, |
|
}; |
|
}, |
|
};
|
|
|