feat(linter): add schema for react/jsx-filename-extension (#23315)

issue #22955

---------

Co-authored-by: Cameron <cameron.clark@hey.com>
This commit is contained in:
Mikhail BaevandCameron authored and GitHub committed 2026-06-12 10:05:03 +01:00
1 parent e6bdfd4c89
commit 0708b5a233
5 files changed
+170 -14

No files matched your search

+18 -1
View File
@@ -176,6 +176,7 @@ export type ForbidItem2 =
message?: string;
};
export type EnforceBooleanAttribute = "always" | "never";
export type JsxFilenameExtensionAllowMode = "always" | "as-needed";
export type FragmentMode = "syntax" | "element";
export type IgnoreEnforceOption = "ignore" | "enforce";
export type NoDidMountSetStateConfig = "allowed" | "disallow-in-func";
@@ -1202,7 +1203,7 @@ export interface DummyRuleMap {
| [AllowWarnDeny, EnforceBooleanAttribute]
| [AllowWarnDeny, EnforceBooleanAttribute, JsxBooleanValueOptions];
"react/jsx-curly-brace-presence"?: DummyRule;
"react/jsx-filename-extension"?: DummyRule;
"react/jsx-filename-extension"?: AllowWarnDeny | [AllowWarnDeny] | [AllowWarnDeny, JsxFilenameExtensionConfig];
"react/jsx-fragments"?: AllowWarnDeny | [AllowWarnDeny] | [AllowWarnDeny, FragmentMode];
"react/jsx-handler-names"?: DummyRule;
"react/jsx-key"?: AllowWarnDeny | [AllowWarnDeny] | [AllowWarnDeny, JsxKeyConfig];
@@ -4152,6 +4153,22 @@ export interface JsxBooleanValueOptions {
*/
never?: string[];
}
export interface JsxFilenameExtensionConfig {
/**
* 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.
*/
allow?: JsxFilenameExtensionAllowMode;
/**
* The set of allowed file extensions.
* Can include or exclude the leading dot (e.g., "jsx" and ".jsx" are both valid).
*/
extensions?: string[];
/**
* If enabled, files that do not contain code (i.e. are empty, contain only whitespaces or comments) will not be rejected.
*/
ignoreFilesWithoutCode?: boolean;
}
export interface JsxKeyConfig {
/**
* When true, check fragment shorthand `<>` for keys
@@ -34,15 +34,15 @@ fn extension_only_for_jsx_diagnostic(ext: &str, span: Span) -> OxcDiagnostic {
#[derive(Debug, Default, Clone, JsonSchema, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
enum AllowType {
/// Always allow a `.jsx` file extension.
enum JsxFilenameExtensionAllowMode {
/// Always allow a JSX filename extension.
#[default]
Always,
/// Only allow `.jsx` file extension for files that contain JSX syntax.
/// Only allow JSX filename extension for files that contain JSX syntax.
AsNeeded,
}
impl AllowType {
impl JsxFilenameExtensionAllowMode {
pub fn from(raw: &str) -> Self {
match raw {
"as-needed" => Self::AsNeeded,
@@ -55,11 +55,11 @@ impl AllowType {
pub struct JsxFilenameExtension(Box<JsxFilenameExtensionConfig>);
#[derive(Debug, Clone, JsonSchema, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", default)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
pub struct JsxFilenameExtensionConfig {
/// 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.
allow: AllowType,
allow: JsxFilenameExtensionAllowMode,
/// The set of allowed file extensions.
/// Can include or exclude the leading dot (e.g., "jsx" and ".jsx" are both valid).
extensions: Vec<CompactStr>,
@@ -70,7 +70,7 @@ pub struct JsxFilenameExtensionConfig {
impl Default for JsxFilenameExtensionConfig {
fn default() -> Self {
Self {
allow: AllowType::Always,
allow: JsxFilenameExtensionAllowMode::Always,
extensions: vec![CompactStr::from("jsx")],
ignore_files_without_code: false,
}
@@ -132,7 +132,7 @@ impl Rule for JsxFilenameExtension {
let allow = config
.and_then(|config| config.get("allow"))
.and_then(Value::as_str)
.map(AllowType::from)
.map(JsxFilenameExtensionAllowMode::from)
.unwrap_or_default();
let extensions = config
@@ -173,7 +173,7 @@ impl Rule for JsxFilenameExtension {
return;
}
if matches!(self.allow, AllowType::AsNeeded) {
if matches!(self.allow, JsxFilenameExtensionAllowMode::AsNeeded) {
if self.ignore_files_without_code && ctx.nodes().len() == 1 {
return;
}
+1 -2
View File
@@ -13,7 +13,7 @@ use crate::rules::RuleEnum;
/// These should return the DummyRule struct/schema instead of the actual rule struct/schema,
/// until we verify that the rule's schema is valid and can be generated by schemars.
#[cfg(feature = "ruledocs")]
const NO_VERIFIED_VALID_SCHEMA: [&str; 40] = [
const NO_VERIFIED_VALID_SCHEMA: [&str; 39] = [
"eslint/func-name-matching",
"eslint/no-restricted-globals",
"eslint/no-restricted-imports",
@@ -36,7 +36,6 @@ const NO_VERIFIED_VALID_SCHEMA: [&str; 40] = [
"node/callback-return",
"react/exhaustive-deps",
"react/jsx-curly-brace-presence",
"react/jsx-filename-extension",
"react/jsx-handler-names",
"react/jsx-no-script-url",
"react/jsx-no-target-blank",
+71 -1
View File
@@ -5572,7 +5572,24 @@
"$ref": "#/definitions/DummyRule"
},
"react/jsx-filename-extension": {
"$ref": "#/definitions/DummyRule"
"anyOf": [
{
"$ref": "#/definitions/AllowWarnDeny"
},
{
"type": "array",
"items": [
{
"$ref": "#/definitions/AllowWarnDeny"
},
{
"$ref": "#/definitions/JsxFilenameExtensionConfig"
}
],
"maxItems": 2,
"minItems": 1
}
]
},
"react/jsx-fragments": {
"anyOf": [
@@ -10301,6 +10318,59 @@
},
"additionalProperties": false
},
"JsxFilenameExtensionAllowMode": {
"oneOf": [
{
"description": "Always allow a JSX filename extension.",
"type": "string",
"enum": [
"always"
],
"markdownDescription": "Always allow a JSX filename extension."
},
{
"description": "Only allow JSX filename extension for files that contain JSX syntax.",
"type": "string",
"enum": [
"as-needed"
],
"markdownDescription": "Only allow JSX filename extension for files that contain JSX syntax."
}
]
},
"JsxFilenameExtensionConfig": {
"type": "object",
"properties": {
"allow": {
"description": "When to allow a JSX filename extension. By default all files may have a JSX extension.\nSet this to `as-needed` to only allow JSX file extensions in files that contain JSX syntax.",
"default": "always",
"allOf": [
{
"$ref": "#/definitions/JsxFilenameExtensionAllowMode"
}
],
"markdownDescription": "When to allow a JSX filename extension. By default all files may have a JSX extension.\nSet this to `as-needed` to only allow JSX file extensions in files that contain JSX syntax."
},
"extensions": {
"description": "The set of allowed file extensions.\nCan include or exclude the leading dot (e.g., \"jsx\" and \".jsx\" are both valid).",
"default": [
"jsx"
],
"type": "array",
"items": {
"type": "string"
},
"markdownDescription": "The set of allowed file extensions.\nCan include or exclude the leading dot (e.g., \"jsx\" and \".jsx\" are both valid)."
},
"ignoreFilesWithoutCode": {
"description": "If enabled, files that do not contain code (i.e. are empty, contain only whitespaces or comments) will not be rejected.",
"default": false,
"type": "boolean",
"markdownDescription": "If enabled, files that do not contain code (i.e. are empty, contain only whitespaces or comments) will not be rejected."
}
},
"additionalProperties": false
},
"JsxKeyConfig": {
"type": "object",
"properties": {
@@ -5576,7 +5576,24 @@ expression: json
"$ref": "#/definitions/DummyRule"
},
"react/jsx-filename-extension": {
"$ref": "#/definitions/DummyRule"
"anyOf": [
{
"$ref": "#/definitions/AllowWarnDeny"
},
{
"type": "array",
"items": [
{
"$ref": "#/definitions/AllowWarnDeny"
},
{
"$ref": "#/definitions/JsxFilenameExtensionConfig"
}
],
"maxItems": 2,
"minItems": 1
}
]
},
"react/jsx-fragments": {
"anyOf": [
@@ -10305,6 +10322,59 @@ expression: json
},
"additionalProperties": false
},
"JsxFilenameExtensionAllowMode": {
"oneOf": [
{
"description": "Always allow a JSX filename extension.",
"type": "string",
"enum": [
"always"
],
"markdownDescription": "Always allow a JSX filename extension."
},
{
"description": "Only allow JSX filename extension for files that contain JSX syntax.",
"type": "string",
"enum": [
"as-needed"
],
"markdownDescription": "Only allow JSX filename extension for files that contain JSX syntax."
}
]
},
"JsxFilenameExtensionConfig": {
"type": "object",
"properties": {
"allow": {
"description": "When to allow a JSX filename extension. By default all files may have a JSX extension.\nSet this to `as-needed` to only allow JSX file extensions in files that contain JSX syntax.",
"default": "always",
"allOf": [
{
"$ref": "#/definitions/JsxFilenameExtensionAllowMode"
}
],
"markdownDescription": "When to allow a JSX filename extension. By default all files may have a JSX extension.\nSet this to `as-needed` to only allow JSX file extensions in files that contain JSX syntax."
},
"extensions": {
"description": "The set of allowed file extensions.\nCan include or exclude the leading dot (e.g., \"jsx\" and \".jsx\" are both valid).",
"default": [
"jsx"
],
"type": "array",
"items": {
"type": "string"
},
"markdownDescription": "The set of allowed file extensions.\nCan include or exclude the leading dot (e.g., \"jsx\" and \".jsx\" are both valid)."
},
"ignoreFilesWithoutCode": {
"description": "If enabled, files that do not contain code (i.e. are empty, contain only whitespaces or comments) will not be rejected.",
"default": false,
"type": "boolean",
"markdownDescription": "If enabled, files that do not contain code (i.e. are empty, contain only whitespaces or comments) will not be rejected."
}
},
"additionalProperties": false
},
"JsxKeyConfig": {
"type": "object",
"properties": {