{"record":{"id":"bdb58f451906a0e6","repo":"denoland/deno","slug":"prefix-linter-plugin-name-must-only-contain-lowe","errorCode":null,"errorMessage":"${prefix}Linter plugin name must only contain lowercase letters (a-z) or hyphens (-).","messagePattern":"(.+?)Linter plugin name must only contain lowercase letters \\(a-z\\) or hyphens \\(-\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/js/40_lint.js","lineNumber":489,"sourceCode":"/**\n * @param {Deno.lint.Plugin} plugin\n * @param {string} [specifier] The specifier the plugin was loaded from, if any.\n */\nfunction installPlugin(plugin, specifier) {\n  // When the plugin was loaded from a specifier (i.e. `lint.plugins` in the\n  // config), prefix validation errors with it so the user knows which plugin\n  // is misbehaving instead of getting an anonymous error.\n  const prefix = typeof specifier === \"string\"\n    ? `Failed to load lint plugin '${specifier}': `\n    : \"\";\n  if (typeof plugin !== \"object\") {\n    throw new Error(`${prefix}Linter plugin must be an object`);\n  }\n  if (typeof plugin.name !== \"string\") {\n    throw new Error(`${prefix}Linter plugin name must be a string`);\n  }\n  if (!/^[a-z-]+$/.test(plugin.name)) {\n    throw new Error(\n      `${prefix}Linter plugin name must only contain lowercase letters (a-z) or hyphens (-).`,\n    );\n  }\n  if (plugin.name.startsWith(\"-\") || plugin.name.endsWith(\"-\")) {\n    throw new Error(\n      `${prefix}Linter plugin name must start and end with a lowercase letter.`,\n    );\n  }\n  if (plugin.name.includes(\"--\")) {\n    throw new Error(\n      `${prefix}Linter plugin name must not have consequtive hyphens.`,\n    );\n  }\n  if (typeof plugin.rules !== \"object\") {\n    throw new Error(`${prefix}Linter plugin rules must be an object`);\n  }\n  if (state.installedPlugins.has(plugin.name)) {\n    throw new Error(`Linter plugin ${plugin.name} has already been registered`);","sourceCodeStart":471,"sourceCodeEnd":507,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/cli/js/40_lint.js#L471-L507","documentation":"Deno's lint plugin loader validates each plugin before installing it into the linter state (installPlugin in cli/js/40_lint.js). A plugin's `name` must match /^[a-z-]+$/ — lowercase letters and hyphens only — because the name becomes part of every rule id (\"plugin-name/rule-name\"). This error is thrown when the name contains anything else: uppercase letters, digits, underscores, dots, or spaces. When the plugin was loaded from a specifier (the `lint.plugins` config), the message is prefixed with \"Failed to load lint plugin '<specifier>': \" so you can identify the misbehaving plugin.","triggerScenarios":"A plugin installed via the lint plugin config/CLI whose default export has `name: \"MyPlugin\"`, `name: \"plugin_1\"`, `name: \"plugin.name\"`, or `name: \"plugin 2\" — any string that fails /^[a-z-]+$/. Validation happens at install time, before any file is linted.","commonSituations":"Porting an ESLint plugin and keeping its camelCase or PascalCase npm package name; generating the name from a file path that contains dots or digits; using underscores or spaces out of habit; copy-pasting a plugin skeleton from another ecosystem.","solutions":["Rename the plugin's `name` to lowercase letters and hyphens only, e.g. \"my-plugin\".","Remove or replace the offending characters (uppercase, digits, _, ., spaces) in the exported name.","If the name is derived from a module specifier or package name, sanitize it before exporting the plugin object.","Re-run `deno lint` and confirm the plugin loads and its rules execute."],"exampleFix":"// before\nexport default {\n  name: \"My_Cool_Plugin\",\n  rules: { \"no-foo\": { create(ctx) { return {}; } } },\n};\n\n// after\nexport default {\n  name: \"my-cool-plugin\",\n  rules: { \"no-foo\": { create(ctx) { return {}; } } },\n};","handlingStrategy":"validation","validationCode":"const PLUGIN_NAME_RE = /^[a-z-]+$/;\nfunction assertValidPluginName(name) {\n  if (typeof name !== \"string\" || !PLUGIN_NAME_RE.test(name)) {\n    throw new Error(\n      `Invalid lint plugin name: ${JSON.stringify(name)} (use lowercase a-z and hyphens only)`,\n    );\n  }\n}","typeGuard":"/** @param {unknown} name @returns {boolean} */\nfunction isValidPluginName(name) {\n  return typeof name === \"string\" && /^[a-z-]+$/.test(name);\n}","tryCatchPattern":"try {\n  await import(\"./my-plugin.js\"); // plugin loaded via lint.plugins\n} catch (err) {\n  // Message carries the \"Failed to load lint plugin '<specifier>': \" prefix\n  console.error(err.message);\n  Deno.exit(1);\n}","preventionTips":["Adopt lowercase-kebab plugin names from the start, mirroring built-in Deno lint rule naming.","Add a smoke test that imports each plugin and asserts /^[a-z-]+$/.test(plugin.name).","Never derive the plugin name from a raw file path or npm package name without sanitizing."],"tags":["deno","deno-lint","lint-plugin","validation","naming"],"backgroundTag":"plugin-validation-failed","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}