@@ -198,6 +198,56 @@ const plugin = {
198198 } ;
199199 } ,
200200 } ) ,
201+ "no-mixed-nullish-coalescing" : defineRule ( {
202+ createOnce ( context ) {
203+ /**
204+ * Returns true for expression node types that, when combined with ??
205+ * without explicit parentheses, create confusing/ambiguous precedence.
206+ * Excluded: UnaryExpression (clearly bound to its operand),
207+ * LogicalExpression with ?? (same operator, unambiguous).
208+ */
209+ const isParenthesized = ( node , source ) => {
210+ // OXC strips ParenthesizedExpression from the AST before visiting,
211+ // so check the raw source surrounding the node's range instead.
212+ const start = node . range ?. [ 0 ] ?? node . start ;
213+ const end = node . range ?. [ 1 ] ?? node . end ;
214+ return source [ start - 1 ] === "(" && source [ end ] === ")" ;
215+ } ;
216+
217+ const isMixedOperatorNode = ( node , source ) => {
218+ if ( isParenthesized ( node , source ) ) return false ;
219+ return (
220+ node . type === "BinaryExpression" ||
221+ ( node . type === "LogicalExpression" && node . operator !== "??" ) ||
222+ node . type === "ConditionalExpression"
223+ ) ;
224+ } ;
225+
226+ return {
227+ LogicalExpression ( node ) {
228+ if ( node . operator !== "??" ) return ;
229+
230+ const source = context . sourceCode . getText ( ) ;
231+
232+ if ( isMixedOperatorNode ( node . left , source ) ) {
233+ context . report ( {
234+ node : node . left ,
235+ message :
236+ "Nullish coalescing (`??`) mixed with other operators without explicit parentheses. Extract to a helper variable or wrap in parentheses for clarity." ,
237+ } ) ;
238+ }
239+
240+ if ( isMixedOperatorNode ( node . right , source ) ) {
241+ context . report ( {
242+ node : node . right ,
243+ message :
244+ "Nullish coalescing (`??`) mixed with other operators without explicit parentheses. Extract to a helper variable or wrap in parentheses for clarity." ,
245+ } ) ;
246+ }
247+ } ,
248+ } ;
249+ } ,
250+ } ) ,
201251 "component-pascal-case" : defineRule ( {
202252 createOnce ( context ) {
203253 const isPascalCase = ( name ) => / ^ [ A - Z ] [ a - z A - Z 0 - 9 ] * $ / . test ( name ) ;
0 commit comments