You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi folks! Wondering if there's been any progress on this issue and/or if I can maybe try to help with it. This is the last rule we still use from ESLint and if it got implemented, we can say bye-bye to it :)
Reacted by Bo Lingen, Jan, Alejandro H, Jeremy Daley and Gualtiero
Reacted by Jordan Cutler, Barthélemy Paléologue, Zihua Li, Mister-Hope, Ryan Tarpine, tpuric, Nick Ames, Maarten Zuidhoorn, Ava Silver, Gualtiero and 1 more
In case anyone needs a temporary solution, it uses the "old" api since using the newer one didn't work with VSCode extension
// naming-convention-plugin.js//// An Oxlint JS plugin that replicates @typescript-eslint/naming-convention.// Written against the standard ESLint-compatible API (`create(context)`),// i.e. a plain plugin object identical in shape to an ESLint plugin. This// variant does NOT depend on "@oxlint/plugins" and works under ESLint as-is.//// Configure in .oxlintrc.json://// {// "jsPlugins": ["./naming-convention-plugin.js"],// "rules": {// "naming-convention/naming-convention": [// "error",// { "selector": "default", "format": ["camelCase"], "leadingUnderscore": "allow" },// { "selector": "typeLike", "format": ["PascalCase"] },// { "selector": "variable", "modifiers": ["const"], "format": ["camelCase", "UPPER_CASE"] }// ]// }// }//// If publishing to npm, this variant has no runtime dependencies.//// ---------------------------------------------------------------------------// SCOPE / LIMITATIONS (read this)://// Faithfully implemented (detectable from the AST alone):// selectors : variable, function, parameter, parameterProperty,// classProperty, objectLiteralProperty, typeProperty,// classMethod, objectLiteralMethod, typeMethod,// classicAccessor, autoAccessor, enumMember,// class, interface, typeAlias, enum, typeParameter, import// + the group selectors default / variableLike / memberLike /// typeLike / method / property / accessor// formats : camelCase, strictCamelCase, PascalCase, StrictPascalCase,// snake_case, UPPER_CASE// modifiers : const, readonly, static, public, private, protected, abstract,// override, async, exported, default, destructured,// requiresQuotes, namespace, global (best-effort), unused (best-effort)// options : format (incl. null), custom {regex,match}, filter (string or// {regex,match}), leadingUnderscore, trailingUnderscore,// prefix, suffix, modifiers, multi-selector arrays,// specificity-based selection.//// NOT supported (require a type-checker, which JS plugins don't have):// - The `types` option (boolean / string / number / function / array).// A selector config carrying `types` is skipped, with a one-time warning.// - `global` and `unused` are best-effort and may differ at edge cases;// `unused` only works if the host exposes sourceCode.getDeclaredVariables.// ---------------------------------------------------------------------------/* ========================================================================== * * Selector bit-flags and meta (group) selectors * ========================================================================== */constS={variable: 1<<0,function: 1<<1,parameter: 1<<2,parameterProperty: 1<<3,classicAccessor: 1<<4,enumMember: 1<<5,classMethod: 1<<6,objectLiteralMethod: 1<<7,typeMethod: 1<<8,classProperty: 1<<9,objectLiteralProperty: 1<<10,typeProperty: 1<<11,autoAccessor: 1<<12,class: 1<<13,interface: 1<<14,typeAlias: 1<<15,enum: 1<<16,typeParameter: 1<<17,import: 1<<18,};constGROUP_VARIABLE_LIKE=S.variable|S.function|S.parameter;constGROUP_MEMBER_LIKE=S.classProperty|S.objectLiteralProperty|S.typeProperty|S.classMethod|S.objectLiteralMethod|S.typeMethod|S.classicAccessor|S.autoAccessor|S.enumMember|S.parameterProperty;constGROUP_TYPE_LIKE=S.class|S.interface|S.typeAlias|S.enum|S.typeParameter;constGROUP_METHOD=S.classMethod|S.objectLiteralMethod|S.typeMethod;constGROUP_PROPERTY=S.classProperty|S.objectLiteralProperty|S.typeProperty;constGROUP_ACCESSOR=S.classicAccessor|S.autoAccessor;constMETA_DEFAULT=-1;// matches everythingconstMETA={default: META_DEFAULT,variableLike: GROUP_VARIABLE_LIKE,memberLike: GROUP_MEMBER_LIKE,typeLike: GROUP_TYPE_LIKE,method: GROUP_METHOD,property: GROUP_PROPERTY,accessor: GROUP_ACCESSOR,};constSELECTOR_LABELS={variable: "Variable",function: "Function",parameter: "Parameter",parameterProperty: "Parameter Property",classicAccessor: "Accessor",autoAccessor: "Accessor",enumMember: "Enum Member",classMethod: "Class Method",objectLiteralMethod: "Object Literal Method",typeMethod: "Type Method",classProperty: "Class Property",objectLiteralProperty: "Object Literal Property",typeProperty: "Type Property",class: "Class",interface: "Interface",typeAlias: "Type Alias",enum: "Enum",typeParameter: "Type Parameter",import: "Import",// group labels (only used if a group config is the matched one)default: "Identifier",variableLike: "Variable",memberLike: "Member",typeLike: "Type",method: "Method",property: "Property",accessor: "Accessor",};functionpopcount(n){// 32-bit population countletx=n>>>0;x=x-((x>>>1)&0x55555555);x=(x&0x33333333)+((x>>>2)&0x33333333);x=(x+(x>>>4))&0x0f0f0f0f;return(x*0x01010101)>>>24;}// Optional diagnostics. Zero overhead unless NC_DEBUG=1 is set in the// environment. Run e.g. `NC_DEBUG=1 oxlint path/to/file.ts` and the rule will// print, to stderr, the parsed config and every identifier it checks (its// computed selector + modifiers + whether a config matched).constDEBUG=typeofprocess!=="undefined"&&!!process.env&&(process.env.NC_DEBUG==="1"||process.env.NC_DEBUG==="true");constFLAG_NAMES=Object.fromEntries(Object.entries(S).map(([k,v])=>[v,k]),);functiondbg(...args){if(DEBUG){// eslint-disable-next-line no-consoleconsole.error("[naming-convention:debug]", ...args);}}/* ========================================================================== * * Format predicates (run on the name AFTER underscores/affixes are trimmed) * ========================================================================== */functionisUppercaseChar(ch){returnch===ch.toUpperCase()&&ch!==ch.toLowerCase();}functionstartsUpper(name){returnname[0]===name[0].toUpperCase()&&name[0]!==name[0].toLowerCase();}functionstartsLower(name){returnname[0]===name[0].toLowerCase()&&name[0]!==name[0].toUpperCase();}// No two consecutive uppercase letters, and no underscores.functionhasStrictCamelHumps(name){if(name.startsWith("_"))returnfalse;letprevUpper=isUppercaseChar(name[0]);for(leti=1;i<name.length;i++){constch=name[i];if(ch==="_")returnfalse;constcurUpper=isUppercaseChar(ch);if(prevUpper&&curUpper)returnfalse;prevUpper=curUpper;}returntrue;}// No leading/trailing underscore, and no consecutive ("__") underscores.functionvalidateUnderscoresInBody(name){if(name.startsWith("_"))returnfalse;letwasUnderscore=false;for(leti=1;i<name.length;i++){if(name[i]==="_"){if(wasUnderscore)returnfalse;wasUnderscore=true;}else{wasUnderscore=false;}}return!wasUnderscore;}constFORMAT_CHECKERS={PascalCase: (name)=>name.length===0||(startsUpper(name)&&!name.includes("_")),StrictPascalCase: (name)=>name.length===0||(startsUpper(name)&&hasStrictCamelHumps(name)),camelCase: (name)=>name.length===0||(startsLower(name)&&!name.includes("_")),strictCamelCase: (name)=>name.length===0||(startsLower(name)&&hasStrictCamelHumps(name)),UPPER_CASE: (name)=>name.length===0||(name===name.toUpperCase()&&validateUnderscoresInBody(name)),snake_case: (name)=>name.length===0||(name===name.toLowerCase()&&validateUnderscoresInBody(name)),};constKNOWN_FORMATS=newSet(Object.keys(FORMAT_CHECKERS));/* ========================================================================== * * Underscore + affix handling (mirrors @typescript-eslint behaviour) * Each returns { valid, name } where `name` has been trimmed for the next step. * ========================================================================== */functionhandleUnderscore(position,option,name){if(!option)return{valid: true, name };constisLeading=position==="leading";consthasSingle=isLeading ? name.startsWith("_") : name.endsWith("_");consthasDouble=isLeading ? name.startsWith("__") : name.endsWith("__");consttrimSingle=()=>(isLeading ? name.slice(1) : name.slice(0,-1));consttrimDouble=()=>(isLeading ? name.slice(2) : name.slice(0,-2));switch(option){case"allow":
return{valid: true,name: hasSingle ? trimSingle() : name};case"allowDouble":
return{valid: true,name: hasDouble ? trimDouble() : name};case"allowSingleOrDouble":
if(hasDouble)return{valid: true,name: trimDouble()};if(hasSingle)return{valid: true,name: trimSingle()};return{valid: true, name };case"require":
returnhasSingle
? {valid: true,name: trimSingle()}
: {valid: false, name };case"requireDouble":
returnhasDouble
? {valid: true,name: trimDouble()}
: {valid: false, name };case"forbid":
return{valid: !hasSingle, name };default:
return{valid: true, name };}}functionhandleAffix(position,affixes,name){if(!affixes||affixes.length===0)return{valid: true, name };constisPrefix=position==="prefix";for(constaffixofaffixes){if(isPrefix ? name.startsWith(affix) : name.endsWith(affix)){consttrimmed=isPrefix
? name.slice(affix.length)
: name.slice(0,name.length-affix.length);return{valid: true,name: trimmed};}}return{valid: false, name };}/* ========================================================================== * * Per-config validator factory * ========================================================================== */functionquote(name){return"`"+name+"`";}functionmakeValidator(cfg){constlabel=cfg.label;returnfunctionvalidate(originalName,reportNode,context){// 1. custom regex (checked against the original, untrimmed name)if(cfg.custom){constmatched=cfg.custom.regex.test(originalName);if(matched!==cfg.custom.match){report(context,reportNode,`${label} name ${quote(originalName)} must ${cfg.custom.match ? "match" : "not match"} the RegExp: /${cfg.custom.regex.source}/`,);return;}}letname=originalName;// 2. leading underscoreletr=handleUnderscore("leading",cfg.leadingUnderscore,name);if(!r.valid){report(context,reportNode,underscoreMsg(label,originalName,"leading",cfg.leadingUnderscore));return;}name=r.name;// 3. trailing underscorer=handleUnderscore("trailing",cfg.trailingUnderscore,name);if(!r.valid){report(context,reportNode,underscoreMsg(label,originalName,"trailing",cfg.trailingUnderscore));return;}name=r.name;// 4. prefixleta=handleAffix("prefix",cfg.prefix,name);if(!a.valid){report(context,reportNode,`${label} name ${quote(originalName)} must have one of the following prefixes: ${cfg.prefix.join(", ")}`,);return;}name=a.name;// 5. suffixa=handleAffix("suffix",cfg.suffix,name);if(!a.valid){report(context,reportNode,`${label} name ${quote(originalName)} must have one of the following suffixes: ${cfg.suffix.join(", ")}`,);return;}name=a.name;// 6. formatif(cfg.format&&cfg.format.length>0){constok=cfg.format.some((f)=>{constchecker=FORMAT_CHECKERS[f];returnchecker ? checker(name) : false;});if(!ok){constformats=cfg.format.join(", ");if(name===originalName){report(context,reportNode,`${label} name ${quote(originalName)} must match one of the following formats: ${formats}`,);}else{report(context,reportNode,`${label} name ${quote(originalName)} trimmed as ${quote(name)} must match one of the following formats: ${formats}`,);}return;}}};}functionunderscoreMsg(label,name,position,option){if(option==="require"||option==="requireDouble"){constcount=option==="requireDouble" ? "two" : "a";return`${label} name ${quote(name)} must have ${count}${position} underscore(s).`;}// forbidreturn`${label} name ${quote(name)} must not have a ${position} underscore.`;}/* ========================================================================== * * Option normalisation + specificity ordering * ========================================================================== */constDEFAULT_OPTIONS=[{selector: "default",format: ["camelCase"],leadingUnderscore: "allow",trailingUnderscore: "allow",},{selector: "import",format: ["camelCase","PascalCase"]},{selector: "variable",format: ["camelCase","UPPER_CASE"],leadingUnderscore: "allow",trailingUnderscore: "allow",},{selector: "typeLike",format: ["PascalCase"]},];letwarnedAboutTypes=false;functionwarnTypesOnce(){if(warnedAboutTypes)return;warnedAboutTypes=true;// eslint-disable-next-line no-consoleconsole.warn("[naming-convention] the `types` option requires type information and "+"is not supported in a JS plugin; selector configs using `types` are ignored.",);}functionnormalizeFilter(filter){if(filter==null)returnnull;if(typeoffilter==="string"){return{regex: newRegExp(filter),match: true};}return{regex: newRegExp(filter.regex),match: filter.match!==false};}functionnormalizeCustom(custom){if(custom==null)returnnull;return{regex: newRegExp(custom.regex),match: custom.match!==false};}functionnormalizeOptions(rawOptions){constnormalized=[];letindex=0;for(constrawofrawOptions){if(!raw||raw.selector==null)continue;constselectors=Array.isArray(raw.selector)
? raw.selector
: [raw.selector];// validate formats up front so misconfig is obviousif(raw.format){for(constfofraw.format){if(!KNOWN_FORMATS.has(f)){// eslint-disable-next-line no-consoleconsole.warn(`[naming-convention] unknown format "${f}" ignored.`);}}}for(constselofselectors){constinS=Object.prototype.hasOwnProperty.call(S,sel);constinMeta=Object.prototype.hasOwnProperty.call(META,sel);if(!inS&&!inMeta){// eslint-disable-next-line no-consoleconsole.warn(`[naming-convention] unknown selector "${sel}" ignored.`);continue;}constbits=inS ? S[sel] : META[sel];constcfg={selector: sel,
bits,isMeta: inMeta,// groups + defaultisDefault: sel==="default",modifiers: Array.isArray(raw.modifiers) ? raw.modifiers.slice() : [],types: Array.isArray(raw.types)&&raw.types.length ? raw.types : null,format: raw.format===undefined ? null : raw.format,custom: normalizeCustom(raw.custom),filter: normalizeFilter(raw.filter),leadingUnderscore: raw.leadingUnderscore||null,trailingUnderscore: raw.trailingUnderscore||null,prefix: Array.isArray(raw.prefix)&&raw.prefix.length ? raw.prefix : null,suffix: Array.isArray(raw.suffix)&&raw.suffix.length ? raw.suffix : null,index: index++,label: SELECTOR_LABELS[sel]||sel,};cfg.validator=makeValidator(cfg);normalized.push(cfg);}}returnnormalized;}// Build, for every individual selector flag, the list of applicable configs// sorted most-specific-first.// tier: individual (2) > group (1) > default (0)// then: more modifiers wins// then: narrower group (fewer covered selectors) wins// then: earlier-declared wins (stable)functionbuildSelectorIndex(normalized){consttier=(c)=>(c.isDefault ? 0 : c.isMeta ? 1 : 2);constcmp=(a,b)=>{constt=tier(b)-tier(a);if(t!==0)returnt;constm=b.modifiers.length-a.modifiers.length;if(m!==0)returnm;if(!a.isDefault&&!b.isDefault){constcov=popcount(a.bits)-popcount(b.bits);// narrower firstif(cov!==0)returncov;}returna.index-b.index;};constmap=newMap();for(constkeyofObject.keys(S)){constflag=S[key];constapplicable=normalized.filter((c)=>c.isDefault||(c.bits&flag)!==0).sort(cmp);map.set(flag,applicable);}returnmap;}/* ========================================================================== * * Name extraction helpers * ========================================================================== */constVALID_IDENT=/^[$A-Z_a-z][$A-Z_a-z0-9]*$/;// Returns { name, node, requiresQuotes } or null when no static name exists.functionkeyInfo(keyNode){if(!keyNode)returnnull;switch(keyNode.type){case"Identifier":
return{name: keyNode.name,node: keyNode,requiresQuotes: false};case"PrivateIdentifier":
return{name: keyNode.name,node: keyNode,requiresQuotes: false};case"Literal": {constv=keyNode.value;constname=String(v);constrequiresQuotes=typeofv==="string" ? !VALID_IDENT.test(name) : true;return{ name,node: keyNode, requiresQuotes };}default:
returnnull;// computed non-literal: no checkable static name}}/* ========================================================================== * * Scope-dependent modifiers (best effort) * ========================================================================== */functionisGlobalScope(node){letcur=node.parent;while(cur){switch(cur.type){case"FunctionDeclaration":
case"FunctionExpression":
case"ArrowFunctionExpression":
case"StaticBlock":
case"BlockStatement":
case"ForStatement":
case"ForInStatement":
case"ForOfStatement":
case"SwitchStatement":
case"CatchClause":
case"ClassDeclaration":
case"ClassExpression":
case"TSModuleDeclaration":
returnfalse;case"Program":
returntrue;default:
cur=cur.parent;}}returnfalse;}functionunusedNames(declNode,context){// best-effort; returns a Set<string> of declared names with zero referencestry{constsc=context&&context.sourceCode;if(!sc||typeofsc.getDeclaredVariables!=="function")returnnull;constvars=sc.getDeclaredVariables(declNode);if(!vars)returnnull;constout=newSet();for(constvofvars){if(v&&Array.isArray(v.references)&&v.references.length===0){out.add(v.name);}}returnout;}catch{returnnull;}}/* ========================================================================== * * The rule * ========================================================================== */construle={meta: {type: "suggestion",docs: {description:
"Enforce naming conventions for everything across a codebase "+"(port of @typescript-eslint/naming-convention).",},schema: {type: "array",items: {type: "object",additionalProperties: true},},messages: {},},// Standard ESLint-compatible API: `create` is called once per file, AFTER// `context.options` has been populated for that file. So (unlike the// `createOnce` variant) we can read options and build the selector index// eagerly here. It is rebuilt per file, which is what ESLint plugins do.create(context){constrawOptions=context.options&&context.options.length>0
? context.options
: DEFAULT_OPTIONS;constnormalized=normalizeOptions(rawOptions);constbySelector=buildSelectorIndex(normalized);if(DEBUG){dbg("options received:",JSON.stringify(context.options),"| usingDefaults:",!(context.options&&context.options.length>0),);dbg("normalized selectors:",normalized.map((c)=>`${c.selector}{mods:[${c.modifiers.join(",")}]${c.types ? ",types(SKIPPED)" : ""}}`,).join(" ")||"(none)",);dbg(`SELF-TEST S.variable=${S.variable} (typeof ${typeofS.variable})`);for(constcofnormalized){constownList=c.isDefault
? "(default/all)"
: (bySelector.get(c.bits)||[]).length;dbg(` cfg selector="${c.selector}" bits=${c.bits} (typeof ${typeofc.bits}) isDefault=${c.isDefault} isMeta=${c.isMeta} ownFlagListLen=${ownList}`,);}dbg(` map keys=[${[...bySelector.keys()].join(",")}]`);}// Pick the most specific config that matches this node, then run it.functioncheck(flag,modifiers,name,reportNode){constlist=bySelector.get(flag);if(DEBUG){dbg(`check ${FLAG_NAMES[flag]||flag} name="${name}" mods=[${[...modifiers].join(",")}] candidates=${list ? list.length : 0}`,);}if(!list)return;for(constcfgoflist){// modifiers: every required modifier must be presentletok=true;for(constmofcfg.modifiers){if(!modifiers.has(m)){ok=false;break;}}if(!ok)continue;// types: unsupported in a JS plugin -> skip this configif(cfg.types){warnTypesOnce();continue;}// filter: gates applicabilityif(cfg.filter){constmatched=cfg.filter.regex.test(name);if(matched!==cfg.filter.match)continue;}if(DEBUG)dbg(` -> matched config selector="${cfg.selector}"`);cfg.validator(name,reportNode,context);return;// first (most specific) match wins}if(DEBUG)dbg(` -> no config matched ${FLAG_NAMES[flag]||flag}`);}/* ---- shared sub-handlers ---------------------------------------- */functioncheckParams(fnNode){constparams=fnNode.params||[];constunused=unusedNames(fnNode,context);for(constpofparams){checkParam(p,unused,false);}}functioncheckParam(p,unused,destructured){if(!p)return;switch(p.type){case"Identifier": {constmods=newSet();if(destructured)mods.add("destructured");if(unused&&unused.has(p.name))mods.add("unused");check(S.parameter,mods,p.name,p);break;}case"AssignmentPattern":
checkParam(p.left,unused,destructured);break;case"RestElement":
checkParam(p.argument,unused,destructured);break;case"ArrayPattern":
for(constelofp.elements)checkParam(el,unused,destructured);break;case"ObjectPattern":
for(constpropofp.properties){if(prop.type==="RestElement"){checkParam(prop.argument,unused,true);}elseif(prop.type==="Property"){// shorthand -> destructured parameter; renamed -> chosen namecheckParam(prop.value,unused,prop.shorthand===true);}}break;case"TSParameterProperty": {constmods=newSet();if(p.accessibility)mods.add(p.accessibility);if(p.readonly)mods.add("readonly");if(p.override)mods.add("override");constinner=p.parameter;consttarget=inner&&inner.type==="AssignmentPattern" ? inner.left : inner;if(target&&target.type==="Identifier"){check(S.parameterProperty,mods,target.name,target);}break;}default:
break;}}// class members (also reused by the TSAbstract* node types)functionhandleClassMember(node,opts){constisAbstract=opts&&opts.abstract;if(node.kind==="constructor")return;// not checkableconstinfo=keyInfo(node.key);if(!info)return;constmods=newSet();if(node.static)mods.add("static");if(node.accessibility)mods.add(node.accessibility);if(node.override)mods.add("override");if(isAbstract)mods.add("abstract");if(info.requiresQuotes)mods.add("requiresQuotes");if(node.type==="PropertyDefinition"){if(node.readonly)mods.add("readonly");if(node.value&&node.value.async)mods.add("async");check(S.classProperty,mods,info.name,info.node);}elseif(node.type==="AccessorProperty"){check(S.autoAccessor,mods,info.name,info.node);}else{// MethodDefinitionif(node.value&&node.value.async)mods.add("async");if(node.kind==="get"||node.kind==="set"){check(S.classicAccessor,mods,info.name,info.node);}else{check(S.classMethod,mods,info.name,info.node);}}}functionhandleClassLike(node){if(!node.id)return;// anonymous (e.g. `export default class {}`)constmods=newSet();if(node.abstract)mods.add("abstract");for(constmofexportModifiers(node))mods.add(m);check(S.class,mods,node.id.name,node.id);}/* ---- visitors ---------------------------------------------------- */return{// Variables -----------------------------------------------------VariableDeclarator(node){constdecl=node.parent;// VariableDeclarationconstkind=decl&&decl.type==="VariableDeclaration" ? decl.kind : "let";constbaseMods=[];if(kind==="const")baseMods.push("const");for(constmofexportModifiers(decl))baseMods.push(m);constasyncInit=node.init&&(node.init.type==="ArrowFunctionExpression"||node.init.type==="FunctionExpression")&&node.init.async;constunused=unusedNames(decl,context);constvisit=(target,destructured)=>{if(!target)return;switch(target.type){case"Identifier": {constmods=newSet(baseMods);if(destructured)mods.add("destructured");if(asyncInit&&!destructured)mods.add("async");if(unused&&unused.has(target.name))mods.add("unused");check(S.variable,mods,target.name,target);break;}case"ObjectPattern":
for(constpropoftarget.properties){if(prop.type==="RestElement"){visit(prop.argument,true);}elseif(prop.type==="Property"){visit(prop.value,prop.shorthand===true);}}break;case"ArrayPattern":
for(consteloftarget.elements)visit(el,false);break;case"AssignmentPattern":
visit(target.left,destructured);break;case"RestElement":
visit(target.argument,destructured);break;default:
break;}};visit(node.id,false);},// Functions -----------------------------------------------------FunctionDeclaration(node){if(node.id){constmods=newSet();if(node.async)mods.add("async");for(constmofexportModifiers(node))mods.add(m);check(S.function,mods,node.id.name,node.id);}checkParams(node);},FunctionExpression(node){// named function expression: `const x = function foo() {}`if(node.id&&(!node.parent||node.parent.type!=="MethodDefinition")){constmods=newSet();if(node.async)mods.add("async");check(S.function,mods,node.id.name,node.id);}checkParams(node);},ArrowFunctionExpression(node){checkParams(node);},// Classes -------------------------------------------------------ClassDeclaration: handleClassLike,ClassExpression: handleClassLike,// Class members -------------------------------------------------MethodDefinition(node){handleClassMember(node,{abstract: false});},PropertyDefinition(node){handleClassMember(node,{abstract: false});},AccessorProperty(node){handleClassMember(node,{abstract: false});},TSAbstractMethodDefinition(node){handleClassMember(node,{abstract: true});},TSAbstractPropertyDefinition(node){handleClassMember(node,{abstract: true});},TSAbstractAccessorProperty(node){handleClassMember(node,{abstract: true});},// Object literal members ---------------------------------------Property(node){// Destructuring targets live inside ObjectPattern; those are handled// by the variable/parameter logic, not here.if(node.parent&&node.parent.type==="ObjectPattern")return;constinfo=keyInfo(node.key);if(!info)return;constmods=newSet();if(info.requiresQuotes)mods.add("requiresQuotes");constval=node.value;constisFn=val&&(val.type==="FunctionExpression"||val.type==="ArrowFunctionExpression");if(node.kind==="get"||node.kind==="set"){check(S.classicAccessor,mods,info.name,info.node);}elseif(node.method&&isFn){if(val.async)mods.add("async");check(S.objectLiteralMethod,mods,info.name,info.node);}else{check(S.objectLiteralProperty,mods,info.name,info.node);}},// Type members --------------------------------------------------TSPropertySignature(node){constinfo=keyInfo(node.key);if(!info)return;constmods=newSet();if(node.readonly)mods.add("readonly");if(info.requiresQuotes)mods.add("requiresQuotes");check(S.typeProperty,mods,info.name,info.node);},TSMethodSignature(node){constinfo=keyInfo(node.key);if(!info)return;constmods=newSet();if(info.requiresQuotes)mods.add("requiresQuotes");check(S.typeMethod,mods,info.name,info.node);},// Enums ---------------------------------------------------------TSEnumDeclaration(node){if(!node.id)return;constmods=newSet();if(node.const)mods.add("const");for(constmofexportModifiers(node))mods.add(m);check(S.enum,mods,node.id.name,node.id);},TSEnumMember(node){constinfo=keyInfo(node.id);if(!info)return;constmods=newSet();if(info.requiresQuotes)mods.add("requiresQuotes");check(S.enumMember,mods,info.name,info.node);},// Other type-likes ---------------------------------------------TSInterfaceDeclaration(node){if(!node.id)return;constmods=newSet(exportModifiers(node));check(S.interface,mods,node.id.name,node.id);},TSTypeAliasDeclaration(node){if(!node.id)return;constmods=newSet(exportModifiers(node));check(S.typeAlias,mods,node.id.name,node.id);},TSTypeParameter(node){constname=node.name&&typeofnode.name==="object"
? node.name.name
: node.name;constreportNode=node.name&&typeofnode.name==="object" ? node.name : node;if(name)check(S.typeParameter,newSet(),name,reportNode);},// Imports -------------------------------------------------------ImportDefaultSpecifier(node){if(node.local){check(S.import,newSet(["default"]),node.local.name,node.local);}},ImportNamespaceSpecifier(node){if(node.local){check(S.import,newSet(["namespace"]),node.local.name,node.local);}},ImportSpecifier(node){if(node.local){check(S.import,newSet(),node.local.name,node.local);}},};},};/* ========================================================================== * * shared helper that needs `isGlobalScope` * ========================================================================== */functionexportModifiers(declNode){constmods=[];constp=declNode&&declNode.parent;if(p&&p.type==="ExportNamedDeclaration")mods.push("exported");if(p&&p.type==="ExportDefaultDeclaration"){mods.push("exported");mods.push("default");}if(declNode&&isGlobalScope(declNode))mods.push("global");returnmods;}functionreport(context,node,message){context.report({ node, message });}/* ========================================================================== * * Plugin export * ========================================================================== */// Plain ESLint-compatible plugin object. No `eslintCompatPlugin` wrapper and// no "@oxlint/plugins" dependency: the rule exposes a standard `create`, so// Oxlint and ESLint consume it identically.constplugin={meta: {name: "naming-convention",},rules: {"naming-convention": rule,},};exportdefaultplugin;// Named exports: useful for unit-testing and reuse. They do not affect how// Oxlint or ESLint consume the plugin (both use the default export).export{FORMAT_CHECKERS,handleUnderscore,handleAffix,normalizeOptions,buildSelectorIndex,S,META,};
Reacted by Fabien Dehopré and Guy Gascoigne-Piggford
Upstream's naming-convention rule has several bugs (definite divergences between docs / behavior) not caught by their existing test suite.
One of these bugs (the astral plane edge case documented in my draft PR #1075 ) would be costly to faithfully replicate, involving simulating UTF-16 lone-surrogate semantics in Go, for a behavior that ~0% of users rely upon. The others are easy enough to implement either way.
In reviewing a PR to implement this rule, are you looking for bug-compatibility with eslint or for an implementation that is true to eslint's documentation?
Reacted by Jan, Andrei Danciu, Lukas Elmer, Bo Lingen, Barthélemy Paléologue, Joey Oliver, Euphysics and Gualtiero
changed the title [-][naming-convention](https://typescript-eslint.io/rules/naming-convention)[/-][+]typescript/naming-convention rule[/+]on Sep 1, 2026
Activity
yqazi27 commentedon Mar 21, 2026
Hi folks! Wondering if there's been any progress on this issue and/or if I can maybe try to help with it. This is the last rule we still use from ESLint and if it got implemented, we can say bye-bye to it :)
sladg commentedon Apr 22, 2026
Hey! also interested, this is last bit blocking couple of my projects from going over to Ox, any timeline would be wonderful (week, month, year?)
feat(linter/eslint): implement `id-match` rule (#22379)
Tyriar commentedon May 31, 2026
Would love this for xterm.js, currently need to run both oxlint and eslint as I don't want to lose automating naming conventions xtermjs/xterm.js#5967
Migrate linting from ESLint to Oxlint (Fixes #5690)
d0z4rt commentedon Jun 24, 2026
In case anyone needs a temporary solution, it uses the "old" api since using the newer one didn't work with VSCode extension
bwbuchanan commentedon Jul 12, 2026
Question for manitainers @Boshen @camc314 :
Upstream's
naming-conventionrule has several bugs (definite divergences between docs / behavior) not caught by their existing test suite.One of these bugs (the astral plane edge case documented in my draft PR #1075 ) would be costly to faithfully replicate, involving simulating UTF-16 lone-surrogate semantics in Go, for a behavior that ~0% of users rely upon. The others are easy enough to implement either way.
In reviewing a PR to implement this rule, are you looking for bug-compatibility with eslint or for an implementation that is true to eslint's documentation?
Kanary-channelio commentedon Aug 10, 2026
Hi @Boshen @camc314, just following up on the question above. Do you happen to have a rough idea of when the direction here might be decided?
[-][naming-convention](https://typescript-eslint.io/rules/naming-convention)[/-][+]typescript/naming-convention rule[/+]vladoyoung commentedon Sep 14, 2026
Bump. This is the only rule that's blocking my team and organization from fully migrating to oxlint.