From 41fb7ef7e7f83abd0b6ab412206457abcc9fddbc Mon Sep 17 00:00:00 2001 From: Yuji Sugiura <6259812+leaysgur@users.noreply.github.com> Date: Sun, 28 Jan 2024 23:25:33 +0900 Subject: [PATCH] chore(tasks/lint_rules): Arrange UI (#2187) Part of #2020 - [x] rule counters - [x] show two tables - [x] omit all the deprecated rules Eventually I decided to split them into 3 tables. In order to be aware of the newly deprecated rules in the future, the deprecated rules are hidden by default, rather than omitted altogether. --- tasks/lint_rules/src/eslint-rules.cjs | 3 + tasks/lint_rules/src/markdown-renderer.cjs | 169 ++++++++++++++++----- 2 files changed, 137 insertions(+), 35 deletions(-) diff --git a/tasks/lint_rules/src/eslint-rules.cjs b/tasks/lint_rules/src/eslint-rules.cjs index d48f9d530be9c..bc642ff9cfcac 100644 --- a/tasks/lint_rules/src/eslint-rules.cjs +++ b/tasks/lint_rules/src/eslint-rules.cjs @@ -68,6 +68,9 @@ const loadPluginTypeScriptRules = (linter) => { const prefixedName = `typescript/${name}`; + // Presented but type is `string | false` + rule.meta.docs.recommended = typeof rule.meta.docs.recommended === "string"; + linter.defineRule(prefixedName, rule); } }; diff --git a/tasks/lint_rules/src/markdown-renderer.cjs b/tasks/lint_rules/src/markdown-renderer.cjs index 7aa810daf0283..5dcd5b439c8c4 100644 --- a/tasks/lint_rules/src/markdown-renderer.cjs +++ b/tasks/lint_rules/src/markdown-renderer.cjs @@ -1,19 +1,55 @@ /** - * @param {string} pluginName - * @param {import("./eslint-rules.cjs").TargetPluginMeta} pluginMeta - * @param {string} listPart + * @typedef {({ name: string } & import("./oxlint-rules.cjs").RuleEntry)} RuleEntryView + * @typedef {{ isImplemented: number; isNotSupported: number; total: number }} CounterView */ -const renderLayout = (pluginName, pluginMeta, listPart) => ` + +/** @param {{ npm: string; }} props */ +const renderIntroduction = ({ npm }) => ` > [!WARNING] > This comment is maintained by CI. Do not edit this comment directly. > To update comment template, see https://github.com/oxc-project/oxc/tree/main/tasks/lint_rules -This is tracking issue for \`${pluginMeta.npm}\`. +This is tracking issue for \`${npm}\`. +`; -## Rules -${listPart} +/** + * @param {{ + * counters: { + * recommended: CounterView; + * notRecommended: CounterView; + * deprecated: CounterView; + * }; + * }} props + */ +const renderCounters = ({ + counters: { recommended, notRecommended, deprecated }, +}) => { + const recommendedTodos = + recommended.total - + (recommended.isImplemented + recommended.isNotSupported); + const notRecommendedTodos = + notRecommended.total - + (notRecommended.isImplemented + notRecommended.isNotSupported); -## Getting started + const countersList = [ + `- ${recommendedTodos}/${recommended.total} recommended rules are remaining as TODO`, + recommendedTodos === 0 && ` - All done! 🎉`, + `- ${notRecommendedTodos}/${notRecommended.total} not recommended rules are remaining as TODO`, + notRecommendedTodos === 0 && ` - All done! 🎉`, + ] + .filter(Boolean) + .join("\n"); + + return ` +There are ${recommended.total + notRecommended.total}(+ ${deprecated.total} deprecated) rules. + +${countersList} +`; +}; + +/** @param {{ pluginName: string }} props */ +const renderGettingStarted = ({ pluginName }) => ` +To get started, run the following command: \`\`\`sh just new-${pluginName}-rule @@ -22,34 +58,34 @@ just new-${pluginName}-rule Then register the rule in \`crates/oxc_linter/src/rules.rs\` and also \`declare_all_lint_rules\` at the bottom. `; -/** @param {[string, import("./oxlint-rules.cjs").RuleEntry][]} ruleEntries */ -const renderRulesList = (ruleEntries) => { - /* prettier-ignore */ - const list = [ - "| Name | Kind | Status | Docs |", - "| :--- | :--: | :----: | :--- |", - ]; - - for (const [name, entry] of ruleEntries) { - // These should be exclusive, but show it for sure... - let kind = ""; - if (entry.isRecommended) kind += "🍀"; - if (entry.isDeprecated) kind += "⚠️"; +/** + * @param {{ + * title: string; + * counters: CounterView; + * views: RuleEntryView[]; + * defaultOpen?: boolean; + * }} props */ +const renderRulesList = ({ title, counters, views, defaultOpen = true }) => ` +## ${title} - let status = ""; - if (entry.isImplemented) status += "✨"; - if (entry.isNotSupported) status += "🚫"; +
+ + ✨: ${counters.isImplemented}, 🚫: ${counters.isNotSupported} / total: ${counters.total} + - list.push(`| ${name} | ${kind} | ${status} | ${entry.docsUrl} |`); - } +| Status | Name | Docs | +| :----: | :--- | :--- | +${views + .map( + (v) => + `| ${v.isImplemented ? "✨" : ""}${v.isNotSupported ? "🚫" : ""} | ${v.name} | ${v.docsUrl} |`, + ) + .join("\n")} - return ` -- Kind: 🍀 = recommended | ⚠️ = deprecated -- Status: ✨ = implemented | 🚫 = not supported +✨ = Implemented, 🚫 = Not supported -${list.join("\n")} +
`; -}; /** * @param {string} pluginName @@ -57,8 +93,71 @@ ${list.join("\n")} * @param {import("./oxlint-rules.cjs").RuleEntries} ruleEntries */ exports.renderMarkdown = (pluginName, pluginMeta, ruleEntries) => { - const pluginRules = Array.from(ruleEntries).filter(([name]) => - name.startsWith(`${pluginName}/`), - ); - return renderLayout(pluginName, pluginMeta, renderRulesList(pluginRules)); + /** @type {Record} */ + const views = { + deprecated: [], + recommended: [], + notRecommended: [], + }; + const counters = { + deprecated: { isImplemented: 0, isNotSupported: 0, total: 0 }, + recommended: { isImplemented: 0, isNotSupported: 0, total: 0 }, + notRecommended: { isImplemented: 0, isNotSupported: 0, total: 0 }, + }; + + for (const [name, entry] of ruleEntries) { + if (!name.startsWith(`${pluginName}/`)) continue; + + let viewsRef, counterRef; + + switch (true) { + case entry.isDeprecated: { + viewsRef = views.deprecated; + counterRef = counters.deprecated; + break; + } + case entry.isRecommended: { + viewsRef = views.recommended; + counterRef = counters.recommended; + break; + } + default: { + viewsRef = views.notRecommended; + counterRef = counters.notRecommended; + } + } + + viewsRef.push({ name, ...entry }); + + if (entry.isImplemented) counterRef.isImplemented++; + if (entry.isNotSupported) counterRef.isNotSupported++; + counterRef.total++; + } + + return [ + renderIntroduction({ npm: pluginMeta.npm }), + renderCounters({ counters }), + renderGettingStarted({ pluginName }), + 0 < views.recommended.length && + renderRulesList({ + title: "Recommended rules", + counters: counters.recommended, + views: views.recommended, + }), + 0 < views.notRecommended.length && + renderRulesList({ + title: "Not recommended rules", + counters: counters.notRecommended, + views: views.notRecommended, + }), + 0 < views.deprecated.length && + renderRulesList({ + title: "Deprecated rules", + counters: counters.deprecated, + views: views.deprecated, + defaultOpen: false, + }), + ] + .filter(Boolean) + .join("\n"); };