feat(linter): add schema for node/callback-return (#23615)

- Avoided looping over `array` of `array`, we only want to know the first level of structure for detecting a tuple config
- refactored some code and introduced a `.clone()` at the beginning. Hope this is fine for the rule doc code path. Feeling sometimes stupid without AI quick fixes :')
- enabled `node-callback-return` schema, which is the only rule that accepts a array as a config
  - https://github.com/eslint-community/eslint-plugin-n/blob/master/docs/rules/callback-return.md
This commit is contained in:
Sysix committed 2026-06-20 20:38:31 +00:00
1 parent 6d355ab8b3
commit b638d0ee1d
4 files changed
+71 -43

No files matched your search

@@ -162,6 +162,10 @@ export type NoUnusedVarsFixMode = "off" | "suggestion" | "fix" | "safe-fix";
export type NoUseBeforeDefineConfigJson = Nofunc | NoUseBeforeDefineConfig;
export type Nofunc = "nofunc";
export type Location = "start" | "anywhere";
/**
* The rule takes a single option - an array of possible callback names - which may include object methods. The default callback names are `callback`, `cb`, `next`.
*/
export type CallbackReturn = string[];
/**
* The rule takes a single string option: the name of the error parameter.
*
@@ -1220,7 +1224,7 @@ export interface DummyRuleMap {
"no-void"?: RuleNoConfig | [AllowWarnDeny, NoVoid];
"no-warning-comments"?: RuleNoConfig | [AllowWarnDeny, NoWarningCommentsConfigJson];
"no-with"?: RuleNoConfig;
"node/callback-return"?: DummyRule;
"node/callback-return"?: RuleNoConfig | [AllowWarnDeny, CallbackReturn];
"node/global-require"?: RuleNoConfig;
"node/handle-callback-err"?: RuleNoConfig | [AllowWarnDeny, HandleCallbackErrConfig];
"node/no-exports-assign"?: RuleNoConfig;
+39 -39
View File
@@ -357,18 +357,24 @@ impl JsonSchema for OxlintRules {
schema: &'a Schema,
r#gen: &'a SchemaGenerator,
) -> &'a Schema {
let mut current = schema;
while let Some(next) = r#gen.dereference(current) {
current = next;
}
let Some(current) = r#gen.dereference(schema) else {
return schema;
};
let Schema::Object(obj) = current else {
return current;
return schema;
};
// We only need to dereference array schemas for rule config.
// Reuse the schema for other cases.
if obj.array.is_none() {
// We only need to dereference array schemas with at least 2 entries. A single entry array schema is used for configs which accepts an array.
// 2 entries means that it is a tuple config, and `AllowWarnDeny` needs to be appended for each entry in the tuple.
if obj.array.as_ref().is_none_or(|array| {
array
.items
.as_ref()
.is_none_or(|items| !matches!(items, SingleOrVec::Vec(_)))
}) {
return schema;
}
@@ -401,7 +407,7 @@ impl JsonSchema for OxlintRules {
config_schema: Schema,
r#gen: &mut SchemaGenerator,
) -> Schema {
let Schema::Object(mut obj) = config_schema else {
let Schema::Object(mut obj) = config_schema.clone() else {
return SchemaObject {
instance_type: Some(InstanceType::Array.into()),
array: Some(Box::new(ArrayValidation {
@@ -426,41 +432,35 @@ impl JsonSchema for OxlintRules {
"Expected rule schema to be either an object, an array, or a reference, but not multiple"
);
if let Some(reference) = obj.reference {
return SchemaObject {
instance_type: Some(InstanceType::Array.into()),
array: Some(Box::new(ArrayValidation {
items: Some(SingleOrVec::Vec(vec![
r#gen.subschema_for::<AllowWarnDeny>(),
Schema::Object(SchemaObject {
reference: Some(reference),
..Default::default()
}),
])),
min_items: Some(2),
max_items: Some(2),
..Default::default()
})),
..Default::default()
}
.into();
}
if let Some(array) = obj.array {
let items = match array.items {
None => vec![r#gen.subschema_for::<AllowWarnDeny>()],
Some(SingleOrVec::Single(config)) => {
vec![r#gen.subschema_for::<AllowWarnDeny>(), *config]
}
Some(SingleOrVec::Vec(configs)) => {
let mut items =
Vec::with_capacity(configs.len().saturating_add(1));
items.push(r#gen.subschema_for::<AllowWarnDeny>());
items.extend(configs);
items
debug_assert!(
array.additional_items.is_none(),
"Expected array schema to have no additionalItems for rule config"
);
// We only need to handle the cases where multiple items exists,
// because single item array schema is used for rules which accepts an array as config,
// and we just need to append `AllowWarnDeny` for that case.
let Some(SingleOrVec::Vec(configs)) = array.items else {
return SchemaObject {
instance_type: Some(InstanceType::Array.into()),
array: Some(Box::new(ArrayValidation {
items: Some(SingleOrVec::Vec(vec![
r#gen.subschema_for::<AllowWarnDeny>(),
config_schema,
])),
min_items: Some(2),
max_items: Some(2),
..Default::default()
})),
..Default::default()
}
.into();
};
let mut items = Vec::with_capacity(configs.len().saturating_add(1));
items.push(r#gen.subschema_for::<AllowWarnDeny>());
items.extend(configs);
let config_length = items.len() as u32;
return SchemaObject {
@@ -496,7 +496,7 @@ impl JsonSchema for OxlintRules {
array: Some(Box::new(ArrayValidation {
items: Some(SingleOrVec::Vec(vec![
r#gen.subschema_for::<AllowWarnDeny>(),
Schema::Object(obj),
config_schema,
])),
min_items: Some(2),
max_items: Some(2),
+1 -2
View File
@@ -10,11 +10,10 @@ 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; 10] = [
const NO_VERIFIED_VALID_SCHEMA: [&str; 9] = [
"eslint/no-restricted-globals", // uses spread syntax like [AllowWarnDeny, ...Config]
"eslint/no-restricted-imports", // uses spread syntax like [AllowWarnDeny, ...Config]
"eslint/no-restricted-properties", // uses spread syntax like [AllowWarnDeny, ...Config]
"node/callback-return", // uses array syntax [AllowWarnDeny, string[]]
"jest/prefer-lowercase-title", // has options from other rule #23186
"jest/valid-title", // has options from other rule #23186
"vitest/prefer-lowercase-title", // has options from other rule #23186
+26 -1
View File
@@ -1138,6 +1138,14 @@
},
"additionalProperties": false
},
"CallbackReturn": {
"description": "The rule takes a single option - an array of possible callback names - which may include object methods. The default callback names are `callback`, `cb`, `next`.",
"type": "array",
"items": {
"type": "string"
},
"markdownDescription": "The rule takes a single option - an array of possible callback names - which may include object methods. The default callback names are `callback`, `cb`, `next`."
},
"CapitalizedCommentsOptions": {
"description": "Configuration for the capitalized-comments rule.\n\nThe first element specifies whether comments should `\"always\"` or `\"never\"`\nbegin with a capital letter. The second element is an optional object\ncontaining additional options.",
"type": "array",
@@ -5517,7 +5525,24 @@
"$ref": "#/definitions/RuleNoConfig"
},
"node/callback-return": {
"$ref": "#/definitions/DummyRule"
"anyOf": [
{
"$ref": "#/definitions/RuleNoConfig"
},
{
"type": "array",
"items": [
{
"$ref": "#/definitions/AllowWarnDeny"
},
{
"$ref": "#/definitions/CallbackReturn"
}
],
"maxItems": 2,
"minItems": 2
}
]
},
"node/global-require": {
"$ref": "#/definitions/RuleNoConfig"