From 6612375390f052dc8d7a857933dd96eb0af93646 Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Sat, 8 Aug 2020 15:17:36 +0200 Subject: [PATCH] [New] `jsx-filename-extension`: Add allow option --- docs/rules/jsx-filename-extension.md | 14 ++++++ lib/rules/jsx-filename-extension.js | 57 ++++++++++++----------- tests/lib/rules/jsx-filename-extension.js | 18 +++++++ 3 files changed, 62 insertions(+), 27 deletions(-) diff --git a/docs/rules/jsx-filename-extension.md b/docs/rules/jsx-filename-extension.md index a5866e6ab6..272addb0a1 100644 --- a/docs/rules/jsx-filename-extension.md +++ b/docs/rules/jsx-filename-extension.md @@ -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. + +```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 diff --git a/lib/rules/jsx-filename-extension.js b/lib/rules/jsx-filename-extension.js index fe433d6ecb..b07bc5518e 100644 --- a/lib/rules/jsx-filename-extension.js +++ b/lib/rules/jsx-filename-extension.js @@ -13,6 +13,7 @@ const docsUrl = require('../util/docsUrl'); // ------------------------------------------------------------------------------ const DEFAULTS = { + allow: 'always', extensions: ['.jsx'] }; @@ -32,6 +33,9 @@ module.exports = { schema: [{ type: 'object', properties: { + allow: { + enum: ['always', 'as-needed'] + }, extensions: { type: 'array', items: { @@ -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 === '') { - return; - } - - if (invalidNode) { - return; - } + if (filename === '') { + // 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); } // -------------------------------------------------------------------------- @@ -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)}'` + }); + } } }; } diff --git a/tests/lib/rules/jsx-filename-extension.js b/tests/lib/rules/jsx-filename-extension.js index 6435bfabce..fd325af04b 100644 --- a/tests/lib/rules/jsx-filename-extension.js +++ b/tests/lib/rules/jsx-filename-extension.js @@ -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']}], @@ -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\''}] + }, { + filename: 'notAComponent.js', + code: withJSXElement, + options: [{allow: 'as-needed'}], + errors: [{message: 'JSX not allowed in files with extension \'.js\''}] }, { filename: 'MyComponent.jsx', code: withJSXElement,