UNPKG

eslint-doc-generator

Version:

Automatic documentation generator for ESLint plugins and rules.

85 lines (84 loc) 3.66 kB
import { join, sep, relative, dirname } from 'node:path'; import { RULE_SOURCE } from './types.js'; import { getPluginRoot } from './package-json.js'; export function replaceRulePlaceholder(pathOrPathFunc, ruleName) { return typeof pathOrPathFunc === 'function' ? pathOrPathFunc(ruleName) : pathOrPathFunc.replaceAll('{name}', ruleName); } /** * Account for how Windows paths use backslashes instead of the forward slashes that URLs require. */ function pathToUrl(path) { return path.split(sep).join('/'); } /** * Get the link to a rule's documentation page. * Will be relative to the current page. */ export function getUrlToRule(context, ruleName, ruleSource, pathToFile) { const { options, path, pluginPrefix } = context; const { pathRuleDoc, urlRuleDoc } = options; switch (ruleSource) { case RULE_SOURCE.eslintCore: { return `https://eslint.org/docs/latest/rules/${ruleName}`; } case RULE_SOURCE.thirdPartyPlugin: { // We don't know the documentation URL to third-party plugins. return undefined; } default: { // Fallthrough to remaining logic in function. break; } } // Ignore plugin prefix if it's included in rule name. // While we could display the prefix if we wanted, it definitely cannot be part of the link. const ruleNameWithoutPluginPrefix = ruleName.startsWith(`${pluginPrefix}/`) ? ruleName.slice(pluginPrefix.length + 1) : ruleName; // If the URL is a function, evaluate it. const urlRuleDocFunctionEvaluated = typeof urlRuleDoc === 'function' ? urlRuleDoc(ruleName, pathToUrl(relative(path, pathToFile))) : undefined; const pathRuleDocEvaluated = join(getPluginRoot(path), replaceRulePlaceholder(pathRuleDoc, ruleNameWithoutPluginPrefix)); return ( // If the function returned a URL, use it. urlRuleDocFunctionEvaluated ?? (typeof urlRuleDoc === 'string' ? // Otherwise, use the URL if it's a string. replaceRulePlaceholder(urlRuleDoc, ruleNameWithoutPluginPrefix) : // Finally, fallback to the relative path. pathToUrl(relative(dirname(pathToFile), pathRuleDocEvaluated)))); } /** * Get the markdown link (title and URL) to the rule's documentation. */ export function getLinkToRule(context, ruleName, pathToFile, includeBackticks, includePrefix) { const { plugin, pluginPrefix } = context; const ruleNameWithoutPluginPrefix = ruleName.startsWith(`${pluginPrefix}/`) ? ruleName.slice(pluginPrefix.length + 1) : ruleName; // Determine what plugin this rule comes from. let ruleSource; if (plugin.rules?.[ruleNameWithoutPluginPrefix]) { ruleSource = RULE_SOURCE.self; } else if (ruleName.includes('/')) { // Assume a slash is for the plugin prefix (ESLint core doesn't have any nested rules). ruleSource = RULE_SOURCE.thirdPartyPlugin; } else { ruleSource = RULE_SOURCE.eslintCore; } const ruleNameWithPluginPrefix = ruleName.startsWith(`${pluginPrefix}/`) ? ruleName : ruleSource === RULE_SOURCE.self ? `${pluginPrefix}/${ruleName}` : undefined; const urlToRule = getUrlToRule(context, ruleName, ruleSource, pathToFile); const ruleNameToDisplay = `${includeBackticks ? '`' : ''}${includePrefix && ruleNameWithPluginPrefix ? ruleNameWithPluginPrefix : ruleNameWithoutPluginPrefix}${includeBackticks ? '`' : ''}`; return urlToRule ? `[${ruleNameToDisplay}](${urlToRule})` : ruleNameToDisplay; }