Skip to content

Commit acf7d62

Browse files
Kev-Y-Huangljharb
authored andcommitted
[New] jsx-pascal-case: support allowNamespace option
1 parent 91e21ac commit acf7d62

File tree

4 files changed

+69
-22
lines changed

4 files changed

+69
-22
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
88
### Added
99
* [`jsx-no-target-blank`]: add fixer ([#2862][] @Nokel81)
1010
* [`jsx-pascal-case`]: support minimatch `ignore` option ([#2906][] @bcherny)
11+
* [`jsx-pascal-case`]: support `allowNamespace` option ([#2917][] @kev-y-huang)
1112

1213
### Fixed
1314
* [`jsx-no-constructed-context-values`]: avoid a crash with `as X` TS code ([#2894][] @ljharb)
@@ -29,6 +30,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
2930
[#2929]: https://github.com/yannickcr/eslint-plugin-react/pull/2929
3031
[#2925]: https://github.com/yannickcr/eslint-plugin-react/pull/2925
3132
[#2923]: https://github.com/yannickcr/eslint-plugin-react/pull/2923
33+
[#2917]: https://github.com/yannickcr/eslint-plugin-react/pull/2917
3234
[#2910]: https://github.com/yannickcr/eslint-plugin-react/pull/2910
3335
[#2908]: https://github.com/yannickcr/eslint-plugin-react/pull/2908
3436
[#2906]: https://github.com/yannickcr/eslint-plugin-react/pull/2906

docs/rules/jsx-pascal-case.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,13 @@ Examples of **correct** code for this rule:
4040

4141
```js
4242
...
43-
"react/jsx-pascal-case": [<enabled>, { allowAllCaps: <allowAllCaps>, ignore: <ignore> }]
43+
"react/jsx-pascal-case": [<enabled>, { allowAllCaps: <allowAllCaps>, allowNamespace: <allowNamespace>, ignore: <ignore> }]
4444
...
4545
```
4646

4747
* `enabled`: for enabling the rule. 0=off, 1=warn, 2=error. Defaults to 0.
4848
* `allowAllCaps`: optional boolean set to `true` to allow components name in all caps (default to `false`).
49+
* `allowNamespace`: optional boolean set to `true` to ignore namespaced components (default to `false`).
4950
* `ignore`: optional string-array of component names to ignore during validation (supports [minimatch](https://github.com/isaacs/minimatch)-style globs).
5051

5152
### `allowAllCaps`
@@ -57,6 +58,15 @@ Examples of **correct** code for this rule, when `allowAllCaps` is `true`:
5758
<TEST_COMPONENT />
5859
```
5960

61+
### `allowNamespace`
62+
63+
Examples of **correct** code for this rule, when `allowNamespace` is `true`:
64+
65+
```jsx
66+
<Allowed.div />
67+
<TestComponent.p />
68+
```
69+
6070
## When Not To Use It
6171

6272
If you are not using JSX.

lib/rules/jsx-pascal-case.js

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ function testAllCaps(name) {
6060
return true;
6161
}
6262

63+
function ignoreCheck(ignore, name) {
64+
return ignore.some(
65+
(entry) => name === entry || minimatch(name, entry, {noglobstar: true})
66+
);
67+
}
68+
6369
// ------------------------------------------------------------------------------
6470
// Rule Definition
6571
// ------------------------------------------------------------------------------
@@ -84,6 +90,9 @@ module.exports = {
8490
allowAllCaps: {
8591
type: 'boolean'
8692
},
93+
allowNamespace: {
94+
type: 'boolean'
95+
},
8796
ignore: {
8897
items: [
8998
{
@@ -102,39 +111,43 @@ module.exports = {
102111
create(context) {
103112
const configuration = context.options[0] || {};
104113
const allowAllCaps = configuration.allowAllCaps || false;
114+
const allowNamespace = configuration.allowNamespace || false;
105115
const ignore = configuration.ignore || [];
106116

107117
return {
108118
JSXOpeningElement(node) {
109119
const isCompatTag = jsxUtil.isDOMComponent(node);
110120
if (isCompatTag) return undefined;
111121

112-
let name = elementType(node);
122+
const name = elementType(node);
123+
let checkNames = [name];
124+
let index = 0;
113125

114-
// Get JSXIdentifier if the type is JSXNamespacedName or JSXMemberExpression
115126
if (name.lastIndexOf(':') > -1) {
116-
name = name.substring(name.lastIndexOf(':') + 1);
127+
checkNames = name.split(':');
117128
} else if (name.lastIndexOf('.') > -1) {
118-
name = name.substring(name.lastIndexOf('.') + 1);
129+
checkNames = name.split('.');
119130
}
120131

121-
if (name.length === 1) return undefined;
122-
123-
const isPascalCase = testPascalCase(name);
124-
const isAllowedAllCaps = allowAllCaps && testAllCaps(name);
125-
const isIgnored = ignore.some(
126-
(entry) => name === entry || minimatch(name, entry, {noglobstar: true})
127-
);
128-
129-
if (!isPascalCase && !isAllowedAllCaps && !isIgnored) {
130-
context.report({
131-
node,
132-
messageId: allowAllCaps ? 'usePascalOrSnakeCase' : 'usePascalCase',
133-
data: {
134-
name
135-
}
136-
});
137-
}
132+
do {
133+
const splitName = checkNames[index];
134+
if (splitName.length === 1) return undefined;
135+
const isPascalCase = testPascalCase(splitName);
136+
const isAllowedAllCaps = allowAllCaps && testAllCaps(splitName);
137+
const isIgnored = ignoreCheck(ignore, splitName);
138+
139+
if (!isPascalCase && !isAllowedAllCaps && !isIgnored) {
140+
context.report({
141+
node,
142+
messageId: allowAllCaps ? 'usePascalOrSnakeCase' : 'usePascalCase',
143+
data: {
144+
name: splitName
145+
}
146+
});
147+
break;
148+
}
149+
index++;
150+
} while (index < checkNames.length && !allowNamespace);
138151
}
139152
};
140153
}

tests/lib/rules/jsx-pascal-case.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ ruleTester.run('jsx-pascal-case', rule, {
9090
code: '<H1>Hello!</H1>'
9191
}, {
9292
code: '<Typography.P />'
93+
}, {
94+
code: '<Styled.h1 />',
95+
options: [{allowNamespace: true}]
9396
}],
9497

9598
invalid: [{
@@ -144,5 +147,24 @@ ruleTester.run('jsx-pascal-case', rule, {
144147
messageId: 'usePascalCase',
145148
data: {name: 'Foo_DEPRECATED'}
146149
}]
150+
}, {
151+
code: '<Styled.h1 />',
152+
errors: [{
153+
messageId: 'usePascalCase',
154+
data: {name: 'h1'}
155+
}]
156+
}, {
157+
code: '<$Typography.P />',
158+
errors: [{
159+
messageId: 'usePascalCase',
160+
data: {name: '$Typography'}
161+
}]
162+
}, {
163+
code: '<STYLED.h1 />',
164+
options: [{allowNamespace: true}],
165+
errors: [{
166+
messageId: 'usePascalCase',
167+
data: {name: 'STYLED'}
168+
}]
147169
}]
148170
});

0 commit comments

Comments
 (0)