diff --git a/CHANGELOG.md b/CHANGELOG.md index 91f3ec919a..de5525a056 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel * [`jsx-no-useless-fragments`]: add option to allow single expressions in fragments ([#3006][] @mattdarveniza) * add [`prefer-exact-props`] rule ([#1547][] @jomasti) * [`jsx-no-target-blank`]: add `forms` option ([#1617][] @jaaberg) +* [`jsx-pascal-case`]: add `allowLeadingUnderscore` option ([#3039][] @pangaeatech) ### Fixed * component detection: use `estraverse` to improve component detection ([#2992][] @Wesitos) @@ -25,6 +26,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel * [Docs] [`require-default-props`]: fix small typo ([#2994][] @evsasse) * [Tests] add weekly scheduled smoke tests ([#2963][] @AriPerkkio) +[#3039]: https://github.com/yannickcr/eslint-plugin-react/pull/3039 [#3038]: https://github.com/yannickcr/eslint-plugin-react/pull/3038 [#3036]: https://github.com/yannickcr/eslint-plugin-react/issues/3036 [#3026]: https://github.com/yannickcr/eslint-plugin-react/pull/3026 diff --git a/docs/rules/jsx-pascal-case.md b/docs/rules/jsx-pascal-case.md index 27de599a97..57f7ab04c4 100644 --- a/docs/rules/jsx-pascal-case.md +++ b/docs/rules/jsx-pascal-case.md @@ -40,12 +40,13 @@ Examples of **correct** code for this rule: ```js ... -"react/jsx-pascal-case": [, { allowAllCaps: , allowNamespace: , ignore: }] +"react/jsx-pascal-case": [, { allowAllCaps: , allowNamespace: , allowLeadingUnderscore: , ignore: }] ... ``` * `enabled`: for enabling the rule. 0=off, 1=warn, 2=error. Defaults to 0. * `allowAllCaps`: optional boolean set to `true` to allow components name in all caps (default to `false`). +* `allowLeadingUnderscore`: optional boolean set to `true` to allow components name with that starts with an underscore (default to `false`). * `allowNamespace`: optional boolean set to `true` to ignore namespaced components (default to `false`). * `ignore`: optional string-array of component names to ignore during validation (supports [minimatch](https://github.com/isaacs/minimatch)-style globs). @@ -67,6 +68,19 @@ Examples of **correct** code for this rule, when `allowNamespace` is `true`: ``` +### `allowLeadingUnderscore` + +Examples of **correct** code for this rule, when `allowLeadingUnderscore` is `true`: + +```jsx +<_AllowedComponent /> +<_AllowedComponent> +
+ +``` + +**WARNING:** Adding a leading underscore to the name of a component does **NOT** affect the visibilty or accessibility of that component. Attempting to use leading underscores to enforce privacy of your components is an error. + ## When Not To Use It If you are not using JSX. diff --git a/lib/rules/jsx-pascal-case.js b/lib/rules/jsx-pascal-case.js index 696525dfda..f391d29565 100644 --- a/lib/rules/jsx-pascal-case.js +++ b/lib/rules/jsx-pascal-case.js @@ -90,6 +90,9 @@ module.exports = { allowAllCaps: { type: 'boolean' }, + allowLeadingUnderscore: { + type: 'boolean' + }, allowNamespace: { type: 'boolean' }, @@ -111,6 +114,7 @@ module.exports = { create(context) { const configuration = context.options[0] || {}; const allowAllCaps = configuration.allowAllCaps || false; + const allowLeadingUnderscore = configuration.allowLeadingUnderscore || false; const allowNamespace = configuration.allowNamespace || false; const ignore = configuration.ignore || []; @@ -132,10 +136,12 @@ module.exports = { do { const splitName = checkNames[index]; if (splitName.length === 1) return undefined; - const isPascalCase = testPascalCase(splitName); - const isAllowedAllCaps = allowAllCaps && testAllCaps(splitName); const isIgnored = ignoreCheck(ignore, splitName); + const checkName = allowLeadingUnderscore && splitName.startsWith('_') ? splitName.slice(1) : splitName; + const isPascalCase = testPascalCase(checkName); + const isAllowedAllCaps = allowAllCaps && testAllCaps(checkName); + if (!isPascalCase && !isAllowedAllCaps && !isIgnored) { context.report({ node, diff --git a/tests/lib/rules/jsx-pascal-case.js b/tests/lib/rules/jsx-pascal-case.js index 8e6d5c2e4e..0cbd5a0bf5 100644 --- a/tests/lib/rules/jsx-pascal-case.js +++ b/tests/lib/rules/jsx-pascal-case.js @@ -93,6 +93,12 @@ ruleTester.run('jsx-pascal-case', rule, { }, { code: '', options: [{allowNamespace: true}] + }, { + code: '<_TEST_COMPONENT />', + options: [{allowAllCaps: true, allowLeadingUnderscore: true}] + }, { + code: '<_TestComponent />', + options: [{allowLeadingUnderscore: true}] }], invalid: [{ @@ -127,6 +133,13 @@ ruleTester.run('jsx-pascal-case', rule, { messageId: 'usePascalOrSnakeCase', data: {name: 'TEST_COMPONENT_'} }] + }, { + code: '', + options: [{allowAllCaps: true}], + errors: [{ + messageId: 'usePascalOrSnakeCase', + data: {name: 'TEST-COMPONENT'} + }] }, { code: '<__ />', options: [{allowAllCaps: true}], @@ -134,6 +147,20 @@ ruleTester.run('jsx-pascal-case', rule, { messageId: 'usePascalOrSnakeCase', data: {name: '__'} }] + }, { + code: '<_div />', + options: [{allowLeadingUnderscore: true}], + errors: [{ + messageId: 'usePascalCase', + data: {name: '_div'} + }] + }, { + code: '<__ />', + options: [{allowAllCaps: true, allowLeadingUnderscore: true}], + errors: [{ + messageId: 'usePascalOrSnakeCase', + data: {name: '__'} + }] }, { code: '<$a />', errors: [{