{"record":{"id":"2cc75f1e2797ce04","repo":"denoland/deno","slug":"unknown-attribute-operator-s","errorCode":null,"errorMessage":"Unknown attribute operator: '${s}'","messagePattern":"Unknown attribute operator: '(.+?)'","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/js/40_lint_selector.js","lineNumber":113,"sourceCode":"  switch (s) {\n    case \"=\":\n      return BinOp.Equal;\n    case \"!=\":\n      return BinOp.NotEqual;\n    case \">\":\n      return BinOp.Greater;\n    case \">=\":\n      return BinOp.GreaterThan;\n    case \"<\":\n      return BinOp.Less;\n    case \"<=\":\n      return BinOp.LessThan;\n    case \"~\":\n      return BinOp.Tilde;\n    case \"+\":\n      return BinOp.Plus;\n    default:\n      throw new Error(`Unknown attribute operator: '${s}'`);\n  }\n}\n\nexport class Lexer {\n  token = Token.Word;\n  start = 0;\n  end = 0;\n  ch = 0;\n  i = -1;\n\n  value = \"\";\n\n  /**\n   * @param {string} input\n   */\n  constructor(input) {\n    this.input = input;\n    this.step();","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/cli/js/40_lint_selector.js#L95-L131","documentation":"Thrown by the attribute-operator lookup in the esquery-style selector parser (cli/js/40_lint_selector.js) when a selector uses an operator inside [...] that is not one of the supported ones (=, !=, <, <=, >, >=, ~, +). Lint rule visitor keys are parsed as selectors, so an unsupported operator aborts selector compilation.","triggerScenarios":"A visitor key / selector like \"VariableDeclaration[kind ^= \\\"const\\\"]\", \"[name %= \\\"x\\\"]\", or \"[a $= \\\"b\\\"]\" — substring/caret/dollar regex-style operators from other selector dialects are not implemented.","commonSituations":"Copying an attribute selector syntax from CSS attribute selectors or another AST query tool into a Deno lint plugin rule; assuming esquery's full operator set is supported when this parser implements a subset.","solutions":["Replace the unsupported operator with a supported one, or move the matching logic into the visitor callback","For substring tests, use a regex value with a supported operator, e.g. [name=/foo/] where supported, or filter in code"],"exampleFix":"// before\n\"ObjectExpression[properties.length > 0][key ^= \\\"data\\\"]\"(node) {},\n\n// after\n\"ObjectExpression[properties.length > 0]\"(node) {\n  // do the key-prefix test in the visitor body\n},","handlingStrategy":"validation","validationCode":"const SUPPORTED_ATTR_OPS = new Set([\"=\", \"!=\", \"<\", \"<=\", \">\", \">=\", \"~\", \"+\"]);\nfunction checkAttrOps(selector) {\n  for (const m of selector.matchAll(/\\[([^\\]]+)\\]/g)) {\n    const op = m[1].match(/(?:!=|=|<=|>=|<|>|~|\\+|[^\\s=<>~+]+)/)?.[0];\n    if (op && !SUPPORTED_ATTR_OPS.has(op)) {\n      throw new Error(`unsupported attribute operator '${op}' in '${selector}'`);\n    }\n  }\n}","typeGuard":"function usesOnlySupportedAttrOps(selector) {\n  return ![...selector.matchAll(/[!=<>~^$*]+=/g)].some((m) =>\n    ![\"!=\", \"<=\", \">=\"].includes(m[0])\n  );\n}","tryCatchPattern":null,"preventionTips":["Restrict attribute selectors to the implemented operator set; push substring/prefix matching into visitor code","Smoke-test selectors by linting a fixture file in CI whenever you add a new visitor key"],"tags":["lint","selector","esquery","parser","plugin"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}