Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[New] jsx-filename-extension add "allow" option with "as-needed" #2746

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/rules/jsx-filename-extension.md
Expand Up @@ -20,8 +20,22 @@ function MyComponent() {
}
```

Beware this rule **only** reports JSX syntax, **not** other non-standard syntax such as experimental features or type annotations.

## Rule Options

### `allow` (default: `"always"`)

When to allow a JSX filename extension. By default all files may have a JSX extension. Set this to `as-needed` to only allow JSX file extensions in files that contain JSX syntax.
remcohaszing marked this conversation as resolved.
Show resolved Hide resolved

```js
"rules": {
"react/jsx-filename-extension": [1, { "allow": "as-needed" }]
}
```

### `extensions` (default: `[".jsx"]`)

The set of allowed extensions is configurable. By default '.jsx' is allowed. If you wanted to allow both '.jsx' and '.js', the configuration would be:

```js
Expand Down
57 changes: 30 additions & 27 deletions lib/rules/jsx-filename-extension.js
Expand Up @@ -13,6 +13,7 @@ const docsUrl = require('../util/docsUrl');
// ------------------------------------------------------------------------------

const DEFAULTS = {
allow: 'always',
extensions: ['.jsx']
};

Expand All @@ -32,6 +33,9 @@ module.exports = {
schema: [{
type: 'object',
properties: {
allow: {
enum: ['always', 'as-needed']
},
extensions: {
type: 'array',
items: {
Expand All @@ -44,32 +48,23 @@ module.exports = {
},

create(context) {
let invalidExtension;
let invalidNode;
const filename = context.getFilename();

function getExtensionsConfig() {
return context.options[0] && context.options[0].extensions || DEFAULTS.extensions;
}
let jsxNode;

function handleJSX(node) {
const filename = context.getFilename();
if (filename === '<text>') {
return;
}

if (invalidNode) {
return;
}
if (filename === '<text>') {
// No need to traverse any nodes.
return {};
}

const allowedExtensions = getExtensionsConfig();
const isAllowedExtension = allowedExtensions.some((extension) => filename.slice(-extension.length) === extension);
const allow = (context.options[0] && context.options[0].allow) || DEFAULTS.allow;
const allowedExtensions = (context.options[0] && context.options[0].extensions) || DEFAULTS.extensions;
const isAllowedExtension = allowedExtensions.some((extension) => filename.slice(-extension.length) === extension);

if (isAllowedExtension) {
return;
function handleJSX(node) {
if (!jsxNode) {
jsxNode = node;
}

invalidNode = node;
invalidExtension = path.extname(filename);
}

// --------------------------------------------------------------------------
Expand All @@ -80,15 +75,23 @@ module.exports = {
JSXElement: handleJSX,
JSXFragment: handleJSX,

'Program:exit'() {
if (!invalidNode) {
'Program:exit'(node) {
if (jsxNode) {
if (!isAllowedExtension) {
context.report({
node: jsxNode,
message: `JSX not allowed in files with extension '${path.extname(filename)}'`
});
}
return;
}

context.report({
node: invalidNode,
message: `JSX not allowed in files with extension '${invalidExtension}'`
});
if (isAllowedExtension && allow === 'as-needed') {
context.report({
node,
message: `Only files containing JSX may use the extension '${path.extname(filename)}'`
});
}
}
};
}
Expand Down
18 changes: 18 additions & 0 deletions tests/lib/rules/jsx-filename-extension.js
Expand Up @@ -45,6 +45,14 @@ ruleTester.run('jsx-filename-extension', rule, {
{
filename: 'MyComponent.jsx',
code: withJSXElement
}, {
filename: 'MyComponent.js',
code: withoutJSX,
options: [{allow: 'as-needed'}]
}, {
filename: 'MyComponent.jsx',
code: withJSXElement,
options: [{allow: 'as-needed'}]
}, {
filename: 'MyComponent.js',
options: [{extensions: ['.js', '.jsx']}],
Expand Down Expand Up @@ -74,6 +82,16 @@ ruleTester.run('jsx-filename-extension', rule, {
filename: 'MyComponent.js',
code: withJSXElement,
errors: [{message: 'JSX not allowed in files with extension \'.js\''}]
}, {
filename: 'MyComponent.jsx',
code: withoutJSX,
options: [{allow: 'as-needed'}],
errors: [{message: 'Only files containing JSX may use the extension \'.jsx\''}]
remcohaszing marked this conversation as resolved.
Show resolved Hide resolved
}, {
filename: 'notAComponent.js',
code: withJSXElement,
options: [{allow: 'as-needed'}],
errors: [{message: 'JSX not allowed in files with extension \'.js\''}]
}, {
filename: 'MyComponent.jsx',
code: withJSXElement,
Expand Down