diff --git a/docs/examples/eslint-plugin-test/docs/rules/require-baz.md b/docs/examples/eslint-plugin-test/docs/rules/require-baz.md index c28bc4c4..a149a8cc 100644 --- a/docs/examples/eslint-plugin-test/docs/rules/require-baz.md +++ b/docs/examples/eslint-plugin-test/docs/rules/require-baz.md @@ -1,6 +1,6 @@ # Require using baz (`test/require-baz`) -❌ This rule is deprecated. It was replaced by [`test/prefer-bar`](prefer-bar.md). +❌ This rule has been [deprecated](https://example.com) since v1.0.0 and will be available until v2.0.0. It was replaced by [`test/prefer-bar`](prefer-bar.md) (custom message about this replacement) ([read more](https://example.com)). Custom message about overall deprecation. 🚫 This rule is _disabled_ in the ⌨️ `typescript` config. diff --git a/lib/rule-doc-notices.ts b/lib/rule-doc-notices.ts index 5553ab48..c5a9cebc 100644 --- a/lib/rule-doc-notices.ts +++ b/lib/rule-doc-notices.ts @@ -10,6 +10,7 @@ import { import { findConfigEmoji, getConfigsForRule } from './plugin-configs.js'; import { RuleModule, + DeprecatedInfo, Plugin, ConfigsToRules, ConfigEmojis, @@ -17,11 +18,16 @@ import { NOTICE_TYPE, UrlRuleDocFunction, PathRuleDocFunction, + ReplacedByInfo, } from './types.js'; import { RULE_TYPE, RULE_TYPE_MESSAGES_NOTICES } from './rule-type.js'; import { RuleDocTitleFormat } from './rule-doc-title-format.js'; import { hasOptions } from './rule-options.js'; -import { getLinkToRule, replaceRulePlaceholder } from './rule-link.js'; +import { + getLinkToRule, + getMarkdownLink, + replaceRulePlaceholder, +} from './rule-link.js'; import { toSentenceCase, removeTrailingPeriod, @@ -82,6 +88,63 @@ function configsToNoticeSentence( return sentence; } +function replacedByToNoticeSentence( + deprecated: DeprecatedInfo, + plugin: Plugin, + pluginPrefix: string, + pathPlugin: string, + pathRuleDoc: string | PathRuleDocFunction, + urlRuleDoc: string | UrlRuleDocFunction | undefined, +): string | undefined { + if (!deprecated.replacedBy || deprecated.replacedBy.length === 0) { + return undefined; + } + + function replacedByIterator( + info: ReplacedByInfo, + idx: number, + arr: ReplacedByInfo[], + ): string | undefined { + // A plugin maintainer should at least specify a rule name + if (!info.rule?.name) { + return undefined; + } + + const conjunction = arr.length > 1 && idx === arr.length - 1 ? 'and ' : ''; + + // @todo - generate (deprecated rules) + // using prefix ahead of replacement rule name uses correct replacement rule link 4 + // with nested rule names has the correct links, especially replacement rule link 5 + // with --path-rule-doc has the correct links, especially replacement rule link 5 + const replacementRule = info.rule.url + ? getMarkdownLink(info.rule.name, true, info.rule.url) + : getLinkToRule( + info.rule.name, + plugin, + pluginPrefix, + pathPlugin, + pathRuleDoc, + replaceRulePlaceholder(pathRuleDoc, info.rule.name), + true, + true, + urlRuleDoc, + ); + + const externalPlugin = info.plugin?.name && info.plugin.name !== 'eslint' + ? ` from ${getMarkdownLink(info.plugin.name, false, info.plugin.url)}` + : ''; + + return `${conjunction}${replacementRule}${externalPlugin}${ + info.message ? ` (${info.message})` : '' + }${info.url ? ` (${getMarkdownLink('read more', false, info.url)})` : ''}`; + } + + return `It was replaced by ${deprecated.replacedBy + .map((item, index, array) => replacedByIterator(item, index, array)) + .filter(Boolean) + .join(', ')}.`; +} + // A few individual notices declared here just so they can be reused in multiple notices. const NOTICE_FIXABLE = `${EMOJI_FIXABLE} This rule is automatically fixable by the [\`--fix\` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).`; const NOTICE_HAS_SUGGESTIONS = `${EMOJI_HAS_SUGGESTIONS} This rule is manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).`; @@ -104,6 +167,7 @@ const RULE_NOTICES: { fixable: boolean; hasSuggestions: boolean; urlConfigs?: string; + deprecated: boolean | DeprecatedInfo | undefined; replacedBy: readonly string[] | undefined; plugin: Plugin; pluginPrefix: string; @@ -187,8 +251,8 @@ const RULE_NOTICES: { return `${emojis.join('')} ${sentences}`; }, - // Deprecated notice has optional "replaced by" rules list. [NOTICE_TYPE.DEPRECATED]: ({ + deprecated, replacedBy, plugin, pluginPrefix, @@ -197,6 +261,37 @@ const RULE_NOTICES: { ruleName, urlRuleDoc, }) => { + if (typeof deprecated === 'object') { + const sentenceDeprecated = `${EMOJI_DEPRECATED} This rule ${ + deprecated.deprecatedSince ? 'has been' : 'is' + } ${deprecated.url ? `[deprecated](${deprecated.url})` : 'deprecated'}${ + deprecated.deprecatedSince + ? ` since v${deprecated.deprecatedSince}` + : '' + }${ + deprecated.availableUntil + ? ` and will be available until v${deprecated.availableUntil}` + : '' + }.`; + + const sentenceReplacedBy = replacedByToNoticeSentence( + deprecated, + plugin, + pluginPrefix, + pathPlugin, + pathRuleDoc, + urlRuleDoc, + ); + + const deprecatedMessage = deprecated.message + ? addTrailingPeriod(deprecated.message) + : undefined; + + return [sentenceDeprecated, sentenceReplacedBy, deprecatedMessage] + .filter(Boolean) + .join(' '); + } + const replacementRuleList = (replacedBy ?? []).map((replacementRuleName) => getLinkToRule( replacementRuleName, @@ -210,6 +305,7 @@ const RULE_NOTICES: { urlRuleDoc, ), ); + return `${EMOJI_DEPRECATED} This rule is deprecated.${ replacedBy && replacedBy.length > 0 ? ` It was replaced by ${replacementRuleList.join(', ')}.` @@ -277,7 +373,7 @@ function getNoticesForRule( configsError.length > 0 || configsWarn.length > 0 || configsOff.length > 0, - [NOTICE_TYPE.DEPRECATED]: rule.meta?.deprecated || false, + [NOTICE_TYPE.DEPRECATED]: Boolean(rule.meta?.deprecated) || false, [NOTICE_TYPE.DESCRIPTION]: Boolean(rule.meta?.docs?.description) || false, // Fixable/suggestions. @@ -359,6 +455,7 @@ function getRuleNoticeLines( configsOff, ruleDocNotices, ); + let noticeType: keyof typeof notices; for (noticeType in notices) { @@ -392,7 +489,8 @@ function getRuleNoticeLines( fixable: Boolean(rule.meta?.fixable), hasSuggestions: Boolean(rule.meta?.hasSuggestions), urlConfigs, - replacedBy: rule.meta?.replacedBy, + deprecated: rule.meta?.deprecated, + replacedBy: rule.meta?.replacedBy, // eslint-disable-line @typescript-eslint/no-deprecated plugin, pluginPrefix, pathPlugin, diff --git a/lib/rule-link.ts b/lib/rule-link.ts index f08bbe9f..91d9e3a3 100644 --- a/lib/rule-link.ts +++ b/lib/rule-link.ts @@ -79,6 +79,16 @@ export function getUrlToRule( ); } +export function getMarkdownLink( + text: string, + includeBackticks: boolean, + url?: string, +) { + const displayedText = includeBackticks ? `\`${text}\`` : text; + + return url ? `[${displayedText}](${url})` : displayedText; +} + /** * Get the markdown link (title and URL) to the rule's documentation. */ @@ -124,11 +134,10 @@ export function getLinkToRule( urlRuleDoc, ); - const ruleNameToDisplay = `${includeBackticks ? '`' : ''}${ + const ruleString = includePrefix && ruleNameWithPluginPrefix ? ruleNameWithPluginPrefix - : ruleNameWithoutPluginPrefix - }${includeBackticks ? '`' : ''}`; + : ruleNameWithoutPluginPrefix; - return urlToRule ? `[${ruleNameToDisplay}](${urlToRule})` : ruleNameToDisplay; + return getMarkdownLink(ruleString, includeBackticks, urlToRule); } diff --git a/lib/types.ts b/lib/types.ts index 2c9d9a97..88356284 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -10,6 +10,10 @@ export type Rules = TSESLint.Linter.RulesRecord; export type RuleSeverity = TSESLint.Linter.RuleLevel; +export type DeprecatedInfo = TSESLint.DeprecatedInfo; + +export type ReplacedByInfo = TSESLint.ReplacedByInfo; + export type Config = TSESLint.Linter.Config; export type Plugin = TSESLint.Linter.Plugin; diff --git a/package-lock.json b/package-lock.json index db53246e..623884af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "2.2.2", "license": "ISC", "dependencies": { - "@typescript-eslint/utils": "^8.0.0", + "@typescript-eslint/utils": "^8.34.0", "ajv": "^8.11.2", "change-case": "^5.0.0", "commander": "^14.0.0", @@ -1823,6 +1823,30 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz", + "integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.26.1", + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/typescript-estree": "8.26.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/@typescript-eslint/parser": { "version": "8.26.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.1.tgz", @@ -1848,10 +1872,45 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz", + "integrity": "sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.34.0", + "@typescript-eslint/types": "^8.34.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz", + "integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/scope-manager": { "version": "8.26.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.1.tgz", "integrity": "sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==", + "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.26.1", @@ -1865,6 +1924,22 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz", + "integrity": "sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/@typescript-eslint/type-utils": { "version": "8.26.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.1.tgz", @@ -1889,10 +1964,35 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz", + "integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.26.1", + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/typescript-estree": "8.26.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/@typescript-eslint/types": { "version": "8.26.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.1.tgz", "integrity": "sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==", + "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1906,6 +2006,7 @@ "version": "8.26.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.1.tgz", "integrity": "sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA==", + "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.26.1", @@ -1929,15 +2030,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz", - "integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.0.tgz", + "integrity": "sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==", "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.26.1", - "@typescript-eslint/types": "8.26.1", - "@typescript-eslint/typescript-estree": "8.26.1" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/typescript-estree": "8.34.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1951,10 +2052,98 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz", + "integrity": "sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz", + "integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz", + "integrity": "sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.34.0", + "@typescript-eslint/tsconfig-utils": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz", + "integrity": "sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.34.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@typescript-eslint/visitor-keys": { "version": "8.26.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz", "integrity": "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==", + "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.26.1", @@ -1972,6 +2161,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9710,9 +9900,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", - "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "license": "MIT", "engines": { "node": ">=18.12" @@ -10055,6 +10245,30 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz", + "integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.26.1", + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/typescript-estree": "8.26.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", diff --git a/package.json b/package.json index 828055fe..b121833c 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "test": "node --max-old-space-size=4096 --experimental-vm-modules node_modules/jest/bin/jest.js --colors --coverage" }, "dependencies": { - "@typescript-eslint/utils": "^8.0.0", + "@typescript-eslint/utils": "^8.34.0", "ajv": "^8.11.2", "change-case": "^5.0.0", "commander": "^14.0.0", diff --git a/test/lib/generate/__snapshots__/rule-deprecation-test.ts.snap b/test/lib/generate/__snapshots__/rule-deprecation-test.ts.snap index ec1da1b1..8140157f 100644 --- a/test/lib/generate/__snapshots__/rule-deprecation-test.ts.snap +++ b/test/lib/generate/__snapshots__/rule-deprecation-test.ts.snap @@ -5,9 +5,11 @@ exports[`generate (deprecated rules) replaced by ESLint core rule uses correct r ❌ Deprecated. -| Name | Description | ❌ | -| :----------------------------- | :----------- | :- | -| [no-foo](docs/rules/no-foo.md) | Description. | ❌ | +| Name | Description | ❌ | +| :------------------------------------- | :----------- | :- | +| [no-foo](docs/rules/no-foo.md) | Description. | ❌ | +| [prefer-bar](docs/rules/prefer-bar.md) | Description. | ❌ | +| [prefer-foo](docs/rules/prefer-foo.md) | Description. | ❌ | " `; @@ -21,14 +23,34 @@ exports[`generate (deprecated rules) replaced by ESLint core rule uses correct r " `; +exports[`generate (deprecated rules) replaced by ESLint core rule uses correct replacement rule link 3`] = ` +"# Description (\`test/prefer-foo\`) + +❌ This rule is deprecated. It was replaced by [\`no-unused-vars\`](https://eslint.org/docs/latest/rules/no-unused-vars). + + +" +`; + +exports[`generate (deprecated rules) replaced by ESLint core rule uses correct replacement rule link 4`] = ` +"# Description (\`test/prefer-bar\`) + +❌ This rule is deprecated. It was replaced by [\`no-unused-vars\`](https://eslint.org/docs/latest/rules/no-unused-vars). + + +" +`; + exports[`generate (deprecated rules) replaced by third-party plugin rule uses correct replacement rule link 1`] = ` " ❌ Deprecated. -| Name | Description | ❌ | -| :----------------------------- | :----------- | :- | -| [no-foo](docs/rules/no-foo.md) | Description. | ❌ | +| Name | Description | ❌ | +| :------------------------------------- | :----------- | :- | +| [no-foo](docs/rules/no-foo.md) | Description. | ❌ | +| [prefer-bar](docs/rules/prefer-bar.md) | Description. | ❌ | +| [prefer-foo](docs/rules/prefer-foo.md) | Description. | ❌ | " `; @@ -42,14 +64,33 @@ exports[`generate (deprecated rules) replaced by third-party plugin rule uses co " `; +exports[`generate (deprecated rules) replaced by third-party plugin rule uses correct replacement rule link 3`] = ` +"# Description (\`test/prefer-foo\`) + +❌ This rule is deprecated. It was replaced by \`other-plugin/no-unused-vars\`. + + +" +`; + +exports[`generate (deprecated rules) replaced by third-party plugin rule uses correct replacement rule link 4`] = ` +"# Description (\`test/prefer-bar\`) + +❌ This rule is deprecated. It was replaced by \`other-plugin/no-unused-vars\` from [eslint-plugin-other-plugin](https://example.org/other-plugin). + + +" +`; + exports[`generate (deprecated rules) replaced by third-party plugin rule with same rule name as one of our rules uses correct replacement rule link 1`] = ` " ❌ Deprecated. -| Name | Description | ❌ | -| :----------------------------- | :----------- | :- | -| [no-foo](docs/rules/no-foo.md) | Description. | ❌ | +| Name | Description | ❌ | +| :------------------------------------- | :----------- | :- | +| [no-foo](docs/rules/no-foo.md) | Description. | ❌ | +| [prefer-foo](docs/rules/prefer-foo.md) | Description. | ❌ | " `; @@ -63,18 +104,29 @@ exports[`generate (deprecated rules) replaced by third-party plugin rule with sa " `; +exports[`generate (deprecated rules) replaced by third-party plugin rule with same rule name as one of our rules uses correct replacement rule link 3`] = ` +"# Description (\`test/prefer-foo\`) + +❌ This rule is deprecated. It was replaced by \`other-plugin/prefer-foo\`. + + +" +`; + exports[`generate (deprecated rules) several deprecated rules updates the documentation 1`] = ` " ❌ Deprecated. -| Name | Description | ❌ | -| :----------------------------- | :----------- | :- | -| [no-bar](docs/rules/no-bar.md) | Description. | ❌ | -| [no-baz](docs/rules/no-baz.md) | Description. | ❌ | -| [no-biz](docs/rules/no-biz.md) | Description. | | -| [no-boz](docs/rules/no-boz.md) | Description. | ❌ | -| [no-foo](docs/rules/no-foo.md) | Description. | ❌ | +| Name | Description | ❌ | +| :------------------------------------- | :----------- | :- | +| [no-bar](docs/rules/no-bar.md) | Description. | ❌ | +| [no-baz](docs/rules/no-baz.md) | Description. | ❌ | +| [no-biz](docs/rules/no-biz.md) | Description. | | +| [no-boz](docs/rules/no-boz.md) | Description. | ❌ | +| [no-foo](docs/rules/no-foo.md) | Description. | ❌ | +| [prefer-bar](docs/rules/prefer-bar.md) | Description. | ❌ | +| [prefer-foo](docs/rules/prefer-foo.md) | Description. | ❌ | " `; @@ -122,15 +174,34 @@ exports[`generate (deprecated rules) several deprecated rules updates the docume " `; +exports[`generate (deprecated rules) several deprecated rules updates the documentation 7`] = ` +"# Description (\`test/prefer-foo\`) + +❌ This rule has been [deprecated](https://example.org/blog/non-existant) since v1.0.0 and will be available until v2.0.0. Custom message about overall deprecation. + + +" +`; + +exports[`generate (deprecated rules) several deprecated rules updates the documentation 8`] = ` +"# Description (\`test/prefer-bar\`) + +❌ This rule is deprecated. It was replaced by [\`test/no-bar\`](no-bar.md), and [\`no-baz\`](https://example.org/rules/no-baz.md) (Custom message) ([read more](https://example.org/an-external-url)). + + +" +`; + exports[`generate (deprecated rules) using prefix ahead of replacement rule name uses correct replacement rule link 1`] = ` " ❌ Deprecated. -| Name | Description | ❌ | -| :----------------------------- | :----------- | :- | -| [no-bar](docs/rules/no-bar.md) | Description. | | -| [no-foo](docs/rules/no-foo.md) | Description. | ❌ | +| Name | Description | ❌ | +| :------------------------------------- | :----------- | :- | +| [no-bar](docs/rules/no-bar.md) | Description. | | +| [no-foo](docs/rules/no-foo.md) | Description. | ❌ | +| [prefer-foo](docs/rules/prefer-foo.md) | Description. | ❌ | " `; @@ -151,15 +222,26 @@ exports[`generate (deprecated rules) using prefix ahead of replacement rule name " `; +exports[`generate (deprecated rules) using prefix ahead of replacement rule name uses correct replacement rule link 4`] = ` +"# Description (\`test/prefer-foo\`) + +❌ This rule is deprecated. It was replaced by [\`test/no-bar\`](../no-bar.md). + + +" +`; + exports[`generate (deprecated rules) with --path-rule-doc has the correct links, especially replacement rule link 1`] = ` " ❌ Deprecated. -| Name | Description | ❌ | -| :------------------------------------------------ | :----------- | :- | -| [category/no-bar](docs/category/no-bar/README.md) | Description. | ❌ | -| [category/no-foo](docs/category/no-foo/README.md) | Description. | ❌ | +| Name | Description | ❌ | +| :-------------------------------------------------------- | :----------- | :- | +| [category/no-bar](docs/category/no-bar/README.md) | Description. | ❌ | +| [category/no-foo](docs/category/no-foo/README.md) | Description. | ❌ | +| [category/prefer-bar](docs/category/prefer-bar/README.md) | Description. | ❌ | +| [category/prefer-foo](docs/category/prefer-foo/README.md) | Description. | ❌ | " `; @@ -182,15 +264,35 @@ exports[`generate (deprecated rules) with --path-rule-doc has the correct links, " `; +exports[`generate (deprecated rules) with --path-rule-doc has the correct links, especially replacement rule link 4`] = ` +"# Description (\`test/category/prefer-foo\`) + +❌ This rule is deprecated. It was replaced by [\`test/category/prefer-bar\`](README.md). + + +" +`; + +exports[`generate (deprecated rules) with --path-rule-doc has the correct links, especially replacement rule link 5`] = ` +"# Description (\`test/category/prefer-bar\`) + +❌ This rule is deprecated. It was replaced by [\`test/category/prefer-foo\`](../../../category/prefer-foo/README.md). + + +" +`; + exports[`generate (deprecated rules) with nested rule names has the correct links, especially replacement rule link 1`] = ` " ❌ Deprecated. -| Name | Description | ❌ | -| :----------------------------------------------- | :----------- | :- | -| [category/no-bar](docs/rules/category/no-bar.md) | Description. | ❌ | -| [category/no-foo](docs/rules/category/no-foo.md) | Description. | ❌ | +| Name | Description | ❌ | +| :------------------------------------------------------- | :----------- | :- | +| [category/no-bar](docs/rules/category/no-bar.md) | Description. | ❌ | +| [category/no-foo](docs/rules/category/no-foo.md) | Description. | ❌ | +| [category/prefer-bar](docs/rules/category/prefer-bar.md) | Description. | ❌ | +| [category/prefer-foo](docs/rules/category/prefer-foo.md) | Description. | ❌ | " `; @@ -213,6 +315,24 @@ exports[`generate (deprecated rules) with nested rule names has the correct link " `; +exports[`generate (deprecated rules) with nested rule names has the correct links, especially replacement rule link 4`] = ` +"# Description (\`test/category/prefer-foo\`) + +❌ This rule is deprecated. It was replaced by [\`test/category/no-bar\`](no-bar.md). + + +" +`; + +exports[`generate (deprecated rules) with nested rule names has the correct links, especially replacement rule link 5`] = ` +"# Description (\`test/category/prefer-bar\`) + +❌ This rule is deprecated. It was replaced by [\`test/category/no-foo\`](../../category/no-foo.md). + + +" +`; + exports[`generate (deprecated rules) with no rule doc but --ignore-deprecated-rules omits the rule from the README and does not try to update its non-existent rule doc 1`] = ` " @@ -221,3 +341,44 @@ exports[`generate (deprecated rules) with no rule doc but --ignore-deprecated-ru " `; + +exports[`generate (deprecated rules) with the object type \`DeprecatedInfo\` displays correct information 1`] = ` +" + +❌ Deprecated. + +| Name | Description | ❌ | +| :----------------------------- | :----------- | :- | +| [no-bar](docs/rules/no-bar.md) | Description. | ❌ | +| [no-baz](docs/rules/no-baz.md) | Description. | ❌ | +| [no-foo](docs/rules/no-foo.md) | Description. | ❌ | + +" +`; + +exports[`generate (deprecated rules) with the object type \`DeprecatedInfo\` displays correct information 2`] = ` +"# Description (\`test/no-foo\`) + +❌ This rule has been [deprecated](https://example.org/blog/non-existant) since v1.0.0 and will be available until v2.0.0. Custom message about overall deprecation. + + +" +`; + +exports[`generate (deprecated rules) with the object type \`DeprecatedInfo\` displays correct information 3`] = ` +"# Description (\`test/no-bar\`) + +❌ This rule is deprecated and will be available until v2.0.0. Custom message about overall deprecation. + + +" +`; + +exports[`generate (deprecated rules) with the object type \`DeprecatedInfo\` displays correct information 4`] = ` +"# Description (\`test/no-baz\`) + +❌ This rule is deprecated. It was replaced by [\`test/no-bar\`](no-bar.md), [\`no-baz\`](https://example.org/rules/no-baz.md), [\`test/no-baz\`](../no-baz.md) (Custom message) ([read more](https://example.org/changelog.md)), and [\`@stylistic/indent\`](https://eslint.style/rules/indent) from [@sytlistic/eslint-plugin](https://eslint.style/). + + +" +`; diff --git a/test/lib/generate/rule-deprecation-test.ts b/test/lib/generate/rule-deprecation-test.ts index 922d618f..3a8ca763 100644 --- a/test/lib/generate/rule-deprecation-test.ts +++ b/test/lib/generate/rule-deprecation-test.ts @@ -60,6 +60,47 @@ describe('generate (deprecated rules)', function () { }, create(context) {} }, + 'prefer-foo': { + // With the object type 'DeprecatedInfo' + meta: { + docs: { description: 'Description.' }, + deprecated: { + message: 'Custom message about overall deprecation.', + deprecatedSince: '1.0.0', + availableUntil: '2.0.0', + url: 'https://example.org/blog/non-existant', + }, + }, + create(context) {} + }, + 'prefer-bar': { + // With the object type 'DeprecatedInfo' + meta: { + docs: { description: 'Description.' }, + deprecated: { + replacedBy: [ + { + // should not be present + rule: {} + }, + { + rule: { + name: 'no-bar', + } + }, + { + message: 'Custom message', + url: 'https://example.org/an-external-url', + rule: { + name: 'no-baz', + url: 'https://example.org/rules/no-baz.md' + } + }, + ] + }, + }, + create(context) {} + }, }, configs: {} };`, @@ -72,6 +113,8 @@ describe('generate (deprecated rules)', function () { 'docs/rules/no-baz.md': '', 'docs/rules/no-biz.md': '', 'docs/rules/no-boz.md': '', + 'docs/rules/prefer-foo.md': '', + 'docs/rules/prefer-bar.md': '', // Needed for some of the test infrastructure to work. node_modules: mockFs.load(PATH_NODE_MODULES), @@ -93,6 +136,8 @@ describe('generate (deprecated rules)', function () { expect(readFileSync('docs/rules/no-baz.md', 'utf8')).toMatchSnapshot(); expect(readFileSync('docs/rules/no-biz.md', 'utf8')).toMatchSnapshot(); expect(readFileSync('docs/rules/no-boz.md', 'utf8')).toMatchSnapshot(); + expect(readFileSync('docs/rules/prefer-foo.md', 'utf8')).toMatchSnapshot(); + expect(readFileSync('docs/rules/prefer-bar.md', 'utf8')).toMatchSnapshot(); }); }); @@ -124,6 +169,36 @@ describe('generate (deprecated rules)', function () { }, create(context) {} }, + 'category/prefer-foo': { + meta: { + docs: { description: 'Description.' }, + deprecated: { + replacedBy: [ + { + rule: { + name: 'category/no-bar', // without plugin prefix + } + }, + ] + }, + }, + create(context) {} + }, + 'category/prefer-bar': { + meta: { + docs: { description: 'Description.' }, + deprecated: { + replacedBy: [ + { + rule: { + name: 'test/category/no-foo', // with plugin prefix + } + }, + ] + }, + }, + create(context) {} + }, }, configs: {} };`, @@ -133,6 +208,8 @@ describe('generate (deprecated rules)', function () { 'docs/rules/category/no-foo.md': '', 'docs/rules/category/no-bar.md': '', + 'docs/rules/category/prefer-foo.md': '', + 'docs/rules/category/prefer-bar.md': '', // Needed for some of the test infrastructure to work. node_modules: mockFs.load(PATH_NODE_MODULES), @@ -155,6 +232,12 @@ describe('generate (deprecated rules)', function () { expect( readFileSync('docs/rules/category/no-bar.md', 'utf8'), ).toMatchSnapshot(); + expect( + readFileSync('docs/rules/category/prefer-foo.md', 'utf8'), + ).toMatchSnapshot(); + expect( + readFileSync('docs/rules/category/prefer-bar.md', 'utf8'), + ).toMatchSnapshot(); }); }); @@ -186,6 +269,35 @@ describe('generate (deprecated rules)', function () { }, create(context) {} }, + 'category/prefer-foo': { + meta: { + docs: { description: 'Description.' }, + deprecated: { + replacedBy: [ + { + rule: { + name: 'category/prefer-bar', // without plugin prefix + }, + }, + ], + }, + }, + }, + 'category/prefer-bar': { + meta: { + docs: { description: 'Description.' }, + deprecated: { + replacedBy: [ + { + rule: { + name: 'test/category/prefer-foo', // with plugin prefix + }, + }, + ], + }, + }, + create(context) {} + }, }, configs: {} };`, @@ -195,6 +307,8 @@ describe('generate (deprecated rules)', function () { 'docs/category/no-foo/README.md': '', 'docs/category/no-bar/README.md': '', + 'docs/category/prefer-foo/README.md': '', + 'docs/category/prefer-bar/README.md': '', // Needed for some of the test infrastructure to work. node_modules: mockFs.load(PATH_NODE_MODULES), @@ -217,6 +331,12 @@ describe('generate (deprecated rules)', function () { expect( readFileSync('docs/category/no-bar/README.md', 'utf8'), ).toMatchSnapshot(); + expect( + readFileSync('docs/category/prefer-foo/README.md', 'utf8'), + ).toMatchSnapshot(); + expect( + readFileSync('docs/category/prefer-bar/README.md', 'utf8'), + ).toMatchSnapshot(); }); }); @@ -244,6 +364,21 @@ describe('generate (deprecated rules)', function () { meta: { docs: { description: 'Description.' }, }, create(context) {} }, + 'prefer-foo': { + meta: { + docs: { description: 'Description.' }, + deprecated: { + replacedBy: [ + { + rule: { + name: 'test/no-bar', + }, + }, + ], + }, + }, + create(context) {} + }, }, configs: {} };`, @@ -253,6 +388,7 @@ describe('generate (deprecated rules)', function () { 'docs/rules/no-foo.md': '', 'docs/rules/no-bar.md': '', + 'docs/rules/prefer-foo.md': '', // Needed for some of the test infrastructure to work. node_modules: mockFs.load(PATH_NODE_MODULES), @@ -271,6 +407,7 @@ describe('generate (deprecated rules)', function () { expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot(); expect(readFileSync('docs/rules/no-bar.md', 'utf8')).toMatchSnapshot(); + expect(readFileSync('docs/rules/prefer-foo.md', 'utf8')).toMatchSnapshot(); }); }); @@ -290,6 +427,14 @@ describe('generate (deprecated rules)', function () { meta: { deprecated: true, }, create(context) {} }, + 'no-bar': { + meta: { + deprecated: { + message: 'Custom message about overall deprecation.', + }, + }, + create(context) {} + }, }, configs: {} };`, @@ -334,6 +479,39 @@ describe('generate (deprecated rules)', function () { }, create(context) {} }, + 'prefer-foo': { + meta: { + docs: { description: 'Description.' }, + deprecated: { + replacedBy: [ + { + rule: { + name: 'no-unused-vars', + }, + }, + ], + }, + }, + create(context) {} + }, + 'prefer-bar': { + meta: { + docs: { description: 'Description.' }, + deprecated: { + replacedBy: [ + { + rule: { + name: 'no-unused-vars', + }, + plugin: { + name: 'eslint', + }, + }, + ], + }, + }, + create(context) {} + }, }, };`, @@ -341,6 +519,8 @@ describe('generate (deprecated rules)', function () { '', 'docs/rules/no-foo.md': '', + 'docs/rules/prefer-foo.md': '', + 'docs/rules/prefer-bar.md': '', // Needed for some of the test infrastructure to work. node_modules: mockFs.load(PATH_NODE_MODULES), @@ -357,6 +537,8 @@ describe('generate (deprecated rules)', function () { expect(readFileSync('README.md', 'utf8')).toMatchSnapshot(); expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot(); + expect(readFileSync('docs/rules/prefer-foo.md', 'utf8')).toMatchSnapshot(); + expect(readFileSync('docs/rules/prefer-bar.md', 'utf8')).toMatchSnapshot(); }); }); @@ -380,6 +562,40 @@ describe('generate (deprecated rules)', function () { }, create(context) {} }, + 'prefer-foo': { + meta: { + docs: { description: 'Description.' }, + deprecated: { + replacedBy: [ + { + rule: { + name: 'other-plugin/no-unused-vars' + } + }, + ], + }, + }, + create(context) {} + }, + 'prefer-bar': { + meta: { + docs: { description: 'Description.' }, + deprecated: { + replacedBy: [ + { + rule: { + name: 'other-plugin/no-unused-vars' + }, + plugin: { + name: 'eslint-plugin-other-plugin', + url: 'https://example.org/other-plugin' + } + }, + ], + }, + }, + create(context) {} + }, }, };`, @@ -387,6 +603,8 @@ describe('generate (deprecated rules)', function () { '', 'docs/rules/no-foo.md': '', + 'docs/rules/prefer-foo.md': '', + 'docs/rules/prefer-bar.md': '', // Needed for some of the test infrastructure to work. node_modules: mockFs.load(PATH_NODE_MODULES), @@ -403,6 +621,8 @@ describe('generate (deprecated rules)', function () { expect(readFileSync('README.md', 'utf8')).toMatchSnapshot(); expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot(); + expect(readFileSync('docs/rules/prefer-foo.md', 'utf8')).toMatchSnapshot(); + expect(readFileSync('docs/rules/prefer-bar.md', 'utf8')).toMatchSnapshot(); }); }); @@ -426,6 +646,21 @@ describe('generate (deprecated rules)', function () { }, create(context) {} }, + 'prefer-foo': { + meta: { + docs: { description: 'Description.' }, + deprecated: { + replacedBy: [ + { + rule: { + name: 'other-plugin/prefer-foo' + }, + }, + ], + }, + }, + create(context) {} + }, }, };`, @@ -433,6 +668,7 @@ describe('generate (deprecated rules)', function () { '', 'docs/rules/no-foo.md': '', + 'docs/rules/prefer-foo.md': '', // Needed for some of the test infrastructure to work. node_modules: mockFs.load(PATH_NODE_MODULES), @@ -449,6 +685,113 @@ describe('generate (deprecated rules)', function () { expect(readFileSync('README.md', 'utf8')).toMatchSnapshot(); expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot(); + expect(readFileSync('docs/rules/prefer-foo.md', 'utf8')).toMatchSnapshot(); + }); + }); + + describe('with the object type `DeprecatedInfo`', function () { + beforeEach(function () { + mockFs({ + 'package.json': JSON.stringify({ + name: 'eslint-plugin-test', + exports: 'index.js', + type: 'module', + }), + + 'index.js': ` + export default { + rules: { + 'no-foo': { + meta: { + docs: { description: 'Description.' }, + deprecated: { + message: 'Custom message about overall deprecation.', + deprecatedSince: '1.0.0', + availableUntil: '2.0.0', + url: 'https://example.org/blog/non-existant', + }, + }, + create(context) {} + }, + 'no-bar': { + meta: { + docs: { description: 'Description.' }, + deprecated: { + message: 'Custom message about overall deprecation.', + availableUntil: '2.0.0', + } + }, + create(context) {} + }, + 'no-baz': { + meta: { + docs: { description: 'Description.' }, + deprecated: { + replacedBy: [ + { + // should not be present + rule: {} + }, + { + rule: { + name: 'no-bar', + } + }, + { + rule: { + name: 'no-baz', + url: 'https://example.org/rules/no-baz.md' + } + }, + { + message: 'Custom message', + url: 'https://example.org/changelog.md', + rule: { + name: 'test/no-baz', + } + }, + { + rule: { + name: '@stylistic/indent', + url: 'https://eslint.style/rules/indent', + }, + plugin: { + name: '@sytlistic/eslint-plugin', + url: 'https://eslint.style/', + } + }, + ] + } + }, + create(context) {} + }, + }, + };`, + + 'README.md': + '', + + 'docs/rules/no-foo.md': '', + 'docs/rules/no-bar.md': '', + 'docs/rules/no-baz.md': '', + + // Needed for some of the test infrastructure to work. + node_modules: mockFs.load(PATH_NODE_MODULES), + }); + }); + + afterEach(function () { + mockFs.restore(); + jest.resetModules(); + }); + + it('displays correct information', async function () { + await generate('.'); + + expect(readFileSync('README.md', 'utf8')).toMatchSnapshot(); + expect(readFileSync('docs/rules/no-foo.md', 'utf8')).toMatchSnapshot(); + expect(readFileSync('docs/rules/no-bar.md', 'utf8')).toMatchSnapshot(); + expect(readFileSync('docs/rules/no-baz.md', 'utf8')).toMatchSnapshot(); }); }); });