From d22bb61aba3a8609733c621be84254cf66671c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8C=83=E6=99=BA=E9=B9=8F?= Date: Thu, 6 Aug 2026 21:17:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=9B=E5=BB=BA=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=3D=E5=86=85=E7=BD=AE=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=EF=BC=88=E4=B8=89=E8=AF=AD=E6=B3=A8=E9=87=8A=EF=BC=89+=20?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E9=9D=A2=E6=9D=BF=E5=B8=AE=E5=8A=A9=E5=9B=BE?= =?UTF-8?q?=E6=A0=87=20+=20=E5=BF=AB=E9=80=9F=E5=BC=80=E5=A7=8B=E4=B8=89?= =?UTF-8?q?=E6=AD=A5=E6=96=87=E6=A1=88=E9=87=8D=E5=86=99=20+=20=E5=9B=BE?= =?UTF-8?q?=E6=A0=87=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _AI_USAGE_LOG.md | 6 + ...26-08-06-builtin-config-template-design.md | 298 ++++++++++++++ images/icon.png | Bin 478 -> 65346 bytes src/adapters/eslint.ts | 56 +-- src/adapters/sqlfluff.ts | 16 +- src/adapters/stylelint.ts | 58 +-- src/i18n/messages.ts | 67 ++-- src/rules/builtin-rules.ts | 371 ++++++++++++++++++ src/rules/static-rules.json | 342 ++++++++++++++++ src/views/setupView.js | 22 +- src/views/setupView.ts | 119 +++--- 11 files changed, 1154 insertions(+), 201 deletions(-) create mode 100644 docs/superpowers/specs/2026-08-06-builtin-config-template-design.md create mode 100644 src/rules/builtin-rules.ts diff --git a/_AI_USAGE_LOG.md b/_AI_USAGE_LOG.md index 9d90014..24e4a28 100644 --- a/_AI_USAGE_LOG.md +++ b/_AI_USAGE_LOG.md @@ -179,3 +179,9 @@ | 2026-08-04 20:59 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 → ⑥ 审查验证 | 修复 AI 审查行号显示错误(整文件审查多 1 行):根因是 AI 返回的 findings[].line 为 1 基(基于带行号代码),而 webview/report 按 0 基处理(buildIssueItem/formatLine 显示 line+1、navigate 直接作 0 基跳转),方法级审查因 commands.ts 已转 0 基而正确、两条路径基准不一致。修复:mergeResults 统一将 aiFindings.line 转 0 基(Math.max(0, f.line-1)),reviewMethod 的 aiFindings 偏移由 f.line+methodLine-1 改为 f.line+methodLine(传 1 基文件行号交 mergeResults 转换,与 customRuleResults 语义一致);更新 merger.test.ts 排序断言(aiFindings error:3/warning:6)+ 新增 0 基转换用例 | 中间产物:无;根因追踪:先假设「mergeResults 统一转 0 基」并在 merger.test.ts 改写断言验证(4 passing);全量测试沿用既有惯例临时移开 adapter/extension.test.js 后 70 用例全部通过(新增 1 个转换用例),恢复原文件 | src/merger/merger.ts, src/activation/commands.ts, src/test/merger.test.ts | deepseek-v4-flash | | 2026-08-04 21:20 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 → ⑥ 审查验证 | SQLFluff PRS 解析错误呈现优化:sqlfluff.ts 新增可导出纯函数 buildPRSMessage(正则提取 Found unparsable section 引号内片段、正则失败回退原文、换行单行化 \\n、超 80 字符截断 +...),check() 中 v.code==='PRS' 时 message 改用 i18n 友好提示(含当前生效方言 effectiveDialect=cliDialect??fallbackDialect + 配置指引)、severity 提升为 error(解析失败时后续规则不可信);messages.ts 新增 adapter.sqlfluffPRS 三语 key;新增 sqlfluff-prs.test.ts 4 用例(片段提取/换行单行化/截断/回退)。背景:问题面板报 PRS 显示原始 SQL 片段+重复行号前缀观感差,且 DIALECT_MAP 默认方言已改为 oracle(08-04 20:34 未提交改动)致 MySQL 风格 DDL 误报 PRS(已实证:同一 SQL mysql 方言解析通过、oracle 报 PRS) | 中间产物:测试首次用 msg.length<200 断言截断不稳定(中英模板长度差异致英文下超限,DEBUG 日志确认实际失败的是 truncate 用例而非 fallback 用例)→ 改为断言 !includes(longFragment)(验证全文被截断,不依赖模板总长);全量测试沿用既有惯例临时移开 adapter/extension.test.js 后 74 用例全部通过(新增 4 个 PRS 用例),恢复原文件 | src/adapters/sqlfluff.ts, src/i18n/messages.ts, src/test/sqlfluff-prs.test.ts(新增) | deepseek-v4-flash | | 2026-08-04 21:45 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 → ⑥ 审查验证 | 修复已打开文件不静态分析、无波浪线问题:根因两处——① package.json activationEvents 为空导致插件不自动激活(onDidOpenTextDocument 对激活前已打开的文件永不触发);② activate 未遍历存量 textDocuments。改动:package.json activationEvents 加 onStartupFinished;extension.ts 新增 scheduleAnalysis(统一按 URI 防抖调度器,gate file scheme + markers.enabled)与 analyzeOpenDocuments(遍历已打开文档、活动文件优先、串行 await runStaticAndApply),activate 时调用;onDidChangeTextDocument 改为 clear 波浪线 + 1s 防抖自动重分析;onDidSaveTextDocument 改用 scheduleAnalysis(500) 替换原独立 debounceTimers Map。复用 runStaticAndApply 版本竞态保护,行为不变。设计文档 docs/superpowers/specs/2026-08-04-auto-static-analysis-design.md 已产出 | 中间产物:无(澄清阶段就"并发 vs 串行"向用户说明各 linter 执行成本后定为串行;激活顺序定为活动文件优先);lint/compile 通过 | package.json, src/extension.ts, docs/superpowers/specs/2026-08-04-auto-static-analysis-design.md(新增) | deepseek-v4-flash | +| 2026-08-05 22:10 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 → ⑥ 审查验证 | 设置面板「共通规则」区画面优化:适配器卡片名称旁与图例优先级行末尾新增「?」帮助图标,悬停 tooltip 展示分步配置说明。i18n 新增 setup.adapter.modeLegendHelp 与 pmd/sql/eslint/stylelintHelp 五组三语 key(四段式:前置条件原因 + 内置规则开箱即用 + 主路径步骤 + 全局备选),删除已无引用的 4 组 xxxGuide;setupView.ts 移除 AdapterConfigStatus.guideText 字段及 collectAdapterStatus 赋值、pushConfig i18n payload 注入 5 个 help 字段、HTML 图例行末尾加「?」、CSS 新增 .help-icon 多行 tooltip(attr(data-help)+white-space:pre-line,卡片相对 header 左对齐/图例相对 mode-legend 右对齐防溢出)与 .adapter-card-name-wrap、删除 .adapter-guide 样式;setupView.js renderAdapters 删除 guide div、卡片标题行名称旁渲染「?」并新增 escapeAttr(转义 & < > ",保证含英文双引号的 data-help 属性安全)。lint/compile 通过;全量测试受既有 stylelint-config-recommended 无 exports main 环境问题阻碍(extension 激活失败),改用 stub vscode 的 node 脚本直接校验 i18n 255 key 三语无缺失、新 5 key 存在、旧 4 key 已删 | 设计迭代:v1 分步文案被用户指出「不知道为什么装 Java / ①② 两条路(项目/全局)混淆 / ruleset.xml 用途不明」→ 改四段式;复用现有 guide 文案被否(要新增配置说明);guide 第二句三选一(移除/动态状态提示/精简保留)用户选移除;图例「?」位置三选一(说明行末尾/副标题旁/每项单独)选说明行末尾;HTML 预览请求在计划模式被只读拦截 → 改为 inline 输出,切 build 后落盘 temp 预览文件供审核;实现中发现 escapeHtml(textContent→innerHTML)不转义双引号,英文 help 文案含 " 会使 data-help 属性提前闭合 → 新增 escapeAttr | src/i18n/messages.ts, src/views/setupView.ts, src/views/setupView.js | deepseek-v4-flash | +| 2026-08-05 22:25 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 → ⑥ 审查验证 | 修复图例区「?」位置与 tooltip 方向:根因——.mode-legend-desc 为 width:100% 独占第二行,紧随其后的 .help-icon 被 flex-wrap 挤到第三行且靠左,tooltip 以 right:0 锚定左移图标向右延伸溢出。改动:setupView.ts HTML 把说明文字与「?」包进 .mode-legend-desc-row(display:flex + justify-content:space-between + width:100%),「?」回到第二行行末;CSS 新增 .mode-legend-desc-row、.mode-legend-desc 去掉 width:100%(row 接管整行),图例 tooltip 保持 .mode-legend .help-icon:hover::after { right:0; left:auto } 使提示相对行末图标向左下方延伸。lint/compile 通过 | 中间产物:无(首版 CSS 就写了 right:0 锚定,实际渲染差源自图标被挤到第三行左侧,定位到根因是 flex-wrap + width:100% 独占行) | src/views/setupView.ts | deepseek-v4-flash | +| 2026-08-05 22:55 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 → ⑥ 审查验证 | 「快速开始」三步卡贴近实际重写:现状是卡片原三步(配置 AI / 启用自定义规则 / 保存并测试)暗示必须配 AI 才能审核,但插件静态分析实际开箱即用(onStartupFinished 自动分析+波浪线)、AI 配置只影响 AI 审核引擎、自定义规则为可选增强,且只用静态分析时原三步全灰有误导。改动:i18n 重写 setup.gettingStarted(三步启用代码审核→三步开始使用)与 step1/step2/step3 及 step3Hint 三语;setupView.js _step1Done 判定由 provider+model+apiKey+baseUrl 改为 msg.adapterStatus.some(a=>a.enabled)(任一适配器启用即完成,默认满足,如实反映静态分析开箱即用)。步骤①文案经两轮微调定稿为「插件已内置 Linter 静态分析,开箱即用,也可在共通规则中配置项目/全局规则」三语。lint/compile 通过 | 澄清阶段向用户展示「卡片文案 vs 实际行为」差距表(静态分析开箱即用/AI 仅深度审查/自定义规则可选/只用静态分析时三步全灰误导)后选「贴近实际重写三步」;标题「三步启用」与开箱即用矛盾,用户选改为「三步开始使用」;步骤①文案两轮迭代:初稿「打开代码文件自动静态分析,无需配置」→ 用户要求强调内置、去掉波浪线 →「已内置 Linter 静态分析,开箱即用,无需配置」→ 用户指出「无需配置」有歧义(易误以为不能/不必再配项目配置)→ 定稿补「也可在共通规则中配置项目/全局规则」(候选「只删无需配置」未选) | src/i18n/messages.ts, src/views/setupView.js | deepseek-v4-flash | +| 2026-08-06 19:52 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 | 生成「创建项目配置 = 内置配置(含三语规则注释)」设计书 | 中间产物:SQLFluff 内置非 rules=all 的纠错、ESLint 方案 A/B 抉择(选 B 仅 JS 零依赖)、注释语言三选一(选三语切换)、链接策略从逐条改为每文件单个索引链接、PMD 类别级注释而非 274 条展开 | docs/superpowers/specs/2026-08-06-builtin-config-template-design.md | deepseek-v4-flash | +| 2026-08-06 20:02 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 | 实现「创建项目配置 = 内置配置(含三语规则注释)」,验证 lint/compile/test 全通过 | 中间产物:ESLint 内置 JS 实际 92 条(61+31)而非方案初稿 93/32;Stylelint 顶层静态 import 纯 ESM 包为既有 bug(CJS require 报 No exports main),改为动态 import;PMD 22 条 exclude 缺描述补写进 static-rules.json;SQLFluff 内置非 rules=all 的纠错 | src/rules/builtin-rules.ts(新建) src/adapters/eslint.ts src/adapters/stylelint.ts src/adapters/sqlfluff.ts src/views/setupView.ts src/rules/static-rules.json src/i18n/messages.ts docs/superpowers/specs/2026-08-06-builtin-config-template-design.md | deepseek-v4-flash | +| 2026-08-06 20:37 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 → ⑥ 审查验证 | 修复安装验证发现的 3 个问题:①PMD 创建配置不生成文件(resolvePmdRulesetPath 打包后 __dirname 退级过多,改逐级向上探测 5 级 + getExtension 路径);②static-rules.json 新增 57 条描述中文被 PowerShell stdin 管道 ASCII 编码破坏成 '?'(改 UTF-8 落盘 .mjs 脚本重写修复);③生成 eslint.config.js/.stylelintrc.js 被插件静态分析报 no-undef 'module'(首行加 /* global module */)。重新打包 1.2.0 VSIX | 中间产物:修复②初判为 esbuild 编码问题(bundle 中文存大写 \\uXXXX 转义,运行时解码正常,排除);仓库根因测试生成的 eslint.config.js 覆盖自身 lint 配置致 146 no-undef,已删除 | src/rules/builtin-rules.ts src/rules/static-rules.json docs/superpowers/specs/2026-08-06-builtin-config-template-design.md | deepseek-v4-flash | diff --git a/docs/superpowers/specs/2026-08-06-builtin-config-template-design.md b/docs/superpowers/specs/2026-08-06-builtin-config-template-design.md new file mode 100644 index 0000000..a6ce75f --- /dev/null +++ b/docs/superpowers/specs/2026-08-06-builtin-config-template-design.md @@ -0,0 +1,298 @@ +# 创建项目配置 = 内置配置(含三语规则注释)— Design Spec + +## 1. 概述 + +当用户在 Setup 面板点击「创建项目配置」时,插件在项目根目录生成对应适配器的配置文件(`ruleset.xml` / `.sqlfluff` / `.stylelintrc.js` / `eslint.config.js`)。 + +本设计将生成的"示例模板"改为**与插件内置运行规则完全一致的配置**,并为每条规则附加三语(zh-CN / en / ja)描述注释,帮助用户理解已启用的规则、按相同格式添加新规则。 + +## 2. 背景与问题 + +### 2.1 现状 + +| 适配器 | 内置运行配置 | 当前"创建项目配置"生成内容 | 问题 | +|--------|-------------|---------------------------|------| +| PMD | `jars/pmd/pmd-java-ruleset.xml`(7 类 274 条 + 41 处排除) | 仅引用 2 个类别(bestpractices + codestyle)的示例 | 规则范围显著缩小 | +| SQLFluff | 精选 26 项规则清单 + `max_line_length=80 / indent_unit=space / tab_space_size=4` | `rules = all` + `dialect = mysql` | 范围过大,且与内置不一致 | +| Stylelint | `stylelint-config-recommended`(41 条)+ 27 条 extra | 仅 3 条示例规则 | 规则范围显著缩小 | +| ESLint | `@eslint/js` recommended(61 条)+ 31 条 extra(JS) | 仅 3 条示例规则 | 规则范围显著缩小 | + +### 2.2 核心问题 + +1. **规则缩水**:项目配置文件的优先级高于内置配置(`eslint.ts:105-115`、`stylelint.ts:120-125`、`pmd.ts:89-92`)。一旦创建当前示例模板,实际检查范围从数十条骤降至 3 条(或 2 个类别)。 +2. **不可知**:用户看不到内置规则清单,也不知道配置文件中的规则写法,难以添加新规则。 + +### 2.3 目标 + +- 「创建项目配置」生成的 4 个文件内容 = 插件内置配置,规则范围精确一致。 +- 每条规则带三语描述注释(随插件 `ai.outputLanguage` 切换)。 +- 每个文件头部含一段"如何添加规则"注释 + **单个**"该工具全部规则索引"链接。 +- 内置规则与生成文件永远一致(单一数据源,杜绝漂移)。 + +### 2.4 非目标 + +- 不在 Setup 面板增加规则目录 UI(生成文件本身即规则清单)。 +- 不对 ESLint 生成 TypeScript 专项规则(方案 B:仅 JS,零依赖)。 +- 不将 PMD 274 条规则展开为逐条 ``(保持类别引用结构)。 +- 不新增"全量规则枚举"(各工具全部规则目录)。 + +## 3. 方案设计 + +### 3.1 架构概览 + +``` +adapters/*.ts (内置运行规则) + │ import 共享常量 + ▼ +src/rules/builtin-rules.ts (单一数据源) + │ 4 个生成函数 + ▼ +setupView.ts 模板 → 点击「创建项目配置」→ 写入项目根目录 + │ + └─ 注释语言 getLanguage() / 规则描述 static-rules.json +``` + +### 3.2 共享数据源模块 `src/rules/builtin-rules.ts`(新建) + +从各 adapter 迁出常量,adapter 与模板共用: + +```typescript +import staticRules from './static-rules.json'; +import type { Language } from '../i18n/messages'; + +// —— 共享常量(从 adapters 迁出)—— +export const eslintExtraRules: Record; // 自 eslint.ts +export const eslintExtraTsRules: Record; // 自 eslint.ts(生成文件不含,仅 adapter 使用) +export const stylelintExtraRules: Record; // 自 stylelint.ts +export const BUILTIN_SQLFLUFF_RULES: string; // 自 sqlfluff.ts(26 项) +export function buildBuiltinSqlfluffConfig(dialect: string): string; // 自 sqlfluff.ts(含行宽/缩进设置) + +// —— 规则描述查询(三语)—— +export function getRuleDescription(linter: string, ruleId: string, lang: Language): string | undefined; + +// —— 文档索引链接(每工具仅 1 个)—— +const ESLINT_RULES_URL = 'https://eslint.org/docs/latest/rules/'; +const STYLELINT_RULES_URL = 'https://stylelint.io/user-guide/rules/'; +const SQLFLUFF_RULES_URL = 'https://docs.sqlfluff.com/en/stable/reference/rules.html'; +// PMD 版本取自 staticRules.linterVersion.pmd → `https://docs.pmd-code.org/pmd-doc-/pmd_rules_java.html` + +// —— 生成函数 —— +export function buildEslintProjectConfigText(lang: Language): string; +export async function buildStylelintProjectConfigText(lang: Language): Promise; +export function buildSqlfluffProjectConfigText(dialect: string, lang: Language): string; +export async function buildPmdProjectRulesetText(lang: Language): Promise; +``` + +**依赖注入**: +- `buildEslintProjectConfigText` 内联 `@eslint/js` 的 `configs.recommended.rules`(61 条,core 规则、无插件依赖)。 +- `buildStylelintProjectConfigText` 运行时 `await import('stylelint-config-recommended')`(ESM-only)后合并 extra,序列化为纯对象(零依赖)。 + +### 3.3 生成文件内容规范 + +#### 3.3.1 ESLint `eslint.config.js`(方案 B:仅 JS,零依赖) + +```js +// ============================================================ +// 项目配置 — 与插件内置 ESLint 规则一致(由插件生成) +// 如何添加规则: +// 1. 在 rules 中新增一行,如 'no-alert': 'warn' +// 2. 带参数写法,如 'semi': ['error', 'always'] +// 全部可用规则:https://eslint.org/docs/latest/rules/ +// 注意:本文件仅包含 JS 内置规则。TS 项目如需 TS 专项规则, +// 请安装 typescript-eslint 后自行追加: +// const ts = require('typescript-eslint'); +// module.exports = [ ...本文件配置, ...ts.configs.recommended ]; +// ============================================================ +module.exports = [ + { + rules: { + // --- @eslint/js recommended (61) --- + 'constructor-super': 'error', // 在构造函数中校验 super() 的调用 + 'for-direction': 'error', // 确保 for 循环更新子句朝正确方向移动计数器 + // ...(61 条全量内联) + // --- 插件补充 (31) --- + 'eqeqeq': 'error', // 强制使用 === 和 !== + 'no-eq-null': 'error', // 禁止与 null 使用 == 比较 + // ...(31 条全量内联) + }, + }, +]; +``` + +#### 3.3.2 Stylelint `.stylelintrc.js` + +```js +// ============================================================ +// 项目配置 — 与插件内置 Stylelint 规则一致(由插件生成) +// 如何添加规则:在 rules 中新增一行,如 'indentation': 2 +// 全部可用规则:https://stylelint.io/user-guide/rules/ +// ============================================================ +module.exports = { + rules: { + 'annotation-no-unknown': true, // 禁止未知注解 + // ...(41 条 recommended 全量内联) + 'color-no-invalid-hex': true, // 禁止无效的十六进制颜色 + // ...(27 条 extra 全量内联) + }, +}; +``` + +#### 3.3.3 SQLFluff `.sqlfluff` + +```ini +[sqlfluff] +# 数据库方言:postgres / mysql / bigquery / snowflake 等 +dialect = mysql +# 想启用全部规则时改为 → rules = all +# 内置精选规则清单: +# core 核心规则组(稳定、跨方言的语法类基础规则) +# AM03 禁止隐式交叉连接 +# CV01 ...(逐条:规则码 + 三语描述,共 26 项) +# 全部可用规则:https://docs.sqlfluff.com/en/stable/reference/rules.html +rules = core,AM03,AM05,AM08,CV01,CV02,CV06,CV08,CV12,LT13,LT14,LT15,ST01,ST02,ST04,ST05,ST06,ST07,ST09,ST10,ST11,ST12,RF02,RF04,RF05,RF06 +max_line_length = 80 +indent_unit = space +tab_space_size = 4 +``` + +#### 3.3.4 PMD `ruleset.xml` + +```xml + + + Java Code Review Rules (274 active rules, 7 categories) — 与插件内置规则集一致(由插件生成) + + + + + + + + + + ... + + +``` + +### 3.4 注释策略 + +| 文件 | 注释粒度 | 内容 | +|------|---------|------| +| `eslint.config.js` / `.stylelintrc.js` | 逐条规则 | `规则码 + 三语描述`(尾行 `//` 注释) | +| `.sqlfluff` | 逐条规则 | `rules =` 行上方注释块(`# 规则码 三语描述`) | +| `ruleset.xml` | 类别级 | 7 个 `` 类别 + 41 处 ``(``) | + +- 描述来源:`static-rules.json` 的 `description` / `descriptionZh` / `descriptionJa`,按 `getLanguage()` 取对应语言。 +- exclude 描述:从 `static-rules.json` pmd 段(273 条)按规则名查询;查不到的仅保留名字、不加注释。 +- 链接:**每个文件仅 1 个**"全部可用规则"索引链接,位于文件头注释块;逐条注释不含链接。 +- 注释语言:跟随 `ai.outputLanguage`(`getLanguage()`),三语切换。 + +### 3.5 setupView 改造(`src/views/setupView.ts`) + +- `ADAPTER_METADATA` 的 `configFileTemplate` 类型:`() => string` → `() => string | Promise`。 +- 4 个模板函数全部委托给 `builtin-rules` 生成函数(删掉 `getEslintTemplate` / `getStylelintTemplate` / `getSqlfluffTemplate` / `getPmdRulesetTemplate` 的示例内容)。 +- `handleAdapterConfig`(`setupView.ts:1231`): + ```typescript + const content = await meta.configFileTemplate(); + fs.writeFileSync(filePath, content, 'utf-8'); + ``` +- PMD 规则集读取路径(复用 `pmd.ts:26-44` 的解析逻辑): + `ext.extensionPath/jars/pmd/pmd-java-ruleset.xml`,找不到时回退到 `out/jars` / 源码相对路径。 +- SQLFluff 方言:固定 `mysql`(用户已确认接受)。 + +### 3.6 adapter 常量抽取(`eslint.ts` / `stylelint.ts` / `sqlfluff.ts`) + +| 文件 | 迁出常量 | 改为 | +|------|---------|------| +| `src/adapters/eslint.ts` | `extraRules` / `extraTsRules` | `import { eslintExtraRules, eslintExtraTsRules } from '../rules/builtin-rules'` | +| `src/adapters/stylelint.ts` | `extraRules` | `import { stylelintExtraRules } from '../rules/builtin-rules'` | +| `src/adapters/sqlfluff.ts` | `BUILTIN_SQLFLUFF_RULES` / `buildBuiltinConfig` | 从 `builtin-rules` 导入 | + +### 3.7 static-rules.json 补充(`src/rules/static-rules.json`) + +- **stylelint**:补写 34 条 recommended 规则的三语描述(`annotation-no-unknown`、`at-rule-no-unknown`、`block-no-empty` 等),使 stylelint 段达到 68 条全覆盖(41 recommended + 27 extra)。 +- **sqlfluff**:补 `core` 规则组条目(如"SQLFluff 核心规则组(稳定、跨方言的语法类基础规则)"),使内置 26 项均有描述。 + +### 3.8 i18n 帮助文案(`src/i18n/messages.ts:515-549`) + +更新 4 个 `setup.adapter.*Help` 文案,核心变化: +- 说明「创建项目配置将生成与内置一致的规则文件(含三语规则注释)」。 +- ESLint 文案补充:生成文件仅含 JS 内置规则;TS 项目如需 TS 专项规则,自行安装 `typescript-eslint` 并参考文件头注释追加。 + +## 4. 关键接口定义 + +```typescript +// src/rules/builtin-rules.ts +export const eslintExtraRules: Record; +export const eslintExtraTsRules: Record; +export const stylelintExtraRules: Record; +export const BUILTIN_SQLFLUFF_RULES: string; +export function buildBuiltinSqlfluffConfig(dialect: string): string; +export function getRuleDescription(linter: string, ruleId: string, lang: Language): string | undefined; +export function buildEslintProjectConfigText(lang: Language): string; +export function buildStylelintProjectConfigText(lang: Language): Promise; +export function buildSqlfluffProjectConfigText(dialect: string, lang: Language): string; +export function buildPmdProjectRulesetText(lang: Language): Promise; + +// src/views/setupView.ts(改造) +type ConfigFileTemplate = () => string | Promise; +``` + +## 5. 文件变更清单 + +| 类型 | 文件 | 说明 | +|------|------|------| +| 新建 | `src/rules/builtin-rules.ts` | 共享常量 + 描述查询 + 4 个生成函数 | +| 修改 | `src/adapters/eslint.ts` | 常量改为从 builtin-rules 导入 | +| 修改 | `src/adapters/stylelint.ts` | 常量改为从 builtin-rules 导入;修复既有 CJS/ESM 加载 bug(`stylelint-config-recommended` 为纯 ESM,顶层静态 import 改动态 import) | +| 修改 | `src/adapters/sqlfluff.ts` | 内置清单/构建函数改为从 builtin-rules 导入 | +| 修改 | `src/views/setupView.ts` | 模板函数委托 + configFileTemplate 异步化 + handleAdapterConfig await | +| 修改 | `src/rules/static-rules.json` | stylelint 补 34 条、sqlfluff 补 core、pmd 补 22 条 exclude 规则描述 | +| 修改 | `src/i18n/messages.ts` | 4 个 adapter help 文案更新 | +| 修改 | `docs/superpowers/specs/2026-08-06-builtin-config-template-design.md` | 本设计书 | + +> 实现期发现并修复:`src/adapters/stylelint.ts` 顶层 `import stylelint-config-recommended`(纯 ESM 包)在 CJS 编译下必然报 "No exports main",属既有 bug,阻塞测试套件。已改为动态 `import`(与 `getModule()` 加载 stylelint 一致)。 +> +> 实际规则数量:ESLint 内置 JS 为 **92 条**(js recommended 61 + extra 31,方案初稿误记为 93/32);Stylelint **68 条**;SQLFluff **26 项**;PMD **37 处 exclude 全部可注释**(补写了 22 条缺失描述)。 + +## 9. 验证反馈修复(2026-08-06) + +安装 VSIX 实测发现并修复 3 个问题: + +1. **PMD「创建项目配置」不生成文件**:`resolvePmdRulesetPath` 原用 `path.resolve(__dirname, '..', '..', 'jars', ...)`,在 esbuild 打包后 `__dirname` = `/out`,该路径多退一级找不到 `jars/`(开发时 `out/rules/` 下退两级才正确)。改为从 `__dirname` 逐级向上探测 5 级目录 + `getExtension` 路径,兼容打包/开发两种布局。 +2. **生成的配置里部分注释为 `?`**:新增的 57 条规则描述(stylelint 34 + sqlfluff core + pmd 22)经 PowerShell 管道 `$s | node` 注入 `static-rules.json` 时,中文被 `$OutputEncoding`(ASCII)破坏成字面 `?`。改用 UTF-8 落盘的 .mjs 脚本重写这 57 条 `descriptionZh/descriptionJa` 修复。 +3. **生成的 eslint.config.js / .stylelintrc.js 被插件静态分析报 `[eslint:no-undef] 'module' is not defined`**:两个配置文件是 CommonJS(`module.exports`),内置规则含 `no-undef` 且未声明 Node 全局。在两个生成文件首行加 `/* global module */` 消除误报。 + +另:本仓库根目录因安装验证生成的 `eslint.config.js` 会覆盖仓库自身 lint 配置(致 146 个 no-undef),为测试产物已删除。 + +## 6. 影响范围 + +- 仅影响「创建项目配置」的文件内容与 Setup 面板帮助文案。 +- adapter 运行逻辑不变(仅常量来源变化,行为一致)。 +- 生成的配置文件位于用户项目根目录,不影响本仓库运行。 + +## 7. 验证方案 + +1. `npm run lint` — ESLint 自检 +2. `npm run compile` — tsc 编译 +3. `npm test` — @vscode/test-cli 运行测试 +4. 手工验证:打开 Setup 面板 → 点击 4 个适配器的「创建项目配置」→ 检查生成内容与 §3.3 示例一致、注释随语言切换。 + +## 8. 已知取舍 + +| 项 | 说明 | +|----|------| +| ESLint 仅 JS | TS 项目使用生成配置后,ESLint 用默认 espree 解析 `.ts` 会产生语法解析错误;文件头注释给出追加 `typescript-eslint` 的示例引导。 | +| SQLFluff 方言固定 mysql | 插件设置留空时内置回退方言为 oracle/ansi,与生成文件有差异(用户已确认接受)。 | +| PMD 类别级注释 | 不展开 274 条逐条 ``,保持类别引用结构,注释以 7 类 + 41 处 exclude 为粒度。 | +| 快照行为 | 生成文件 = 内置规则快照;插件日后升级改内置规则时,已生成的项目文件不会自动同步(项目配置优先级最高)。 | + +## 9. 排除范围(非本期) + +- Setup 面板「规则目录」UI(查→复制→粘贴)。 +- 各工具全量规则枚举(超出内置子集的规则)。 +- ESLint TS 专项规则生成。 diff --git a/images/icon.png b/images/icon.png index ba641849a6cb76f0845c4905e090c7388b812590..87cae8ba765e6a64131de5eec22affa1264d34b5 100644 GIT binary patch literal 65346 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 T_~v2&m_NAUYlR9q#HarQBrx`l literal 478 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC#^NA%Cx&(BWL^TKq$yjVB?030`Uiq%YP89;S`KNAl|93z>%b=#3AN1p+)8S+c$NE zDV9Y$Z`a-^Do# ze~vl458|1CdKsBOm}P|nSP@XY!9IrPyIFj=FDO57hv) = { - 'eqeqeq': 'error', - 'no-eq-null': 'error', - 'no-self-compare': 'error', - 'no-promise-executor-return': 'error', - 'no-shadow': 'error', - 'no-unassigned-vars': 'error', - 'no-useless-assignment': 'error', - 'block-scoped-var': 'error', - 'default-case': 'error', - 'default-case-last': 'error', - 'no-unmodified-loop-condition': 'error', - 'no-unreachable-loop': 'error', - 'no-eval': 'error', - 'no-extend-native': 'error', - 'no-var': 'error', - 'no-await-in-loop': 'warn', - 'prefer-template': 'warn', - 'prefer-object-spread': 'warn', - 'prefer-rest-params': 'warn', - 'prefer-spread': 'warn', - 'prefer-object-has-own': 'warn', - 'no-useless-concat': 'warn', - 'no-useless-return': 'warn', - 'no-useless-computed-key': 'warn', - 'no-useless-rename': 'warn', - 'no-param-reassign': 'warn', - 'no-return-assign': 'error', - 'no-throw-literal': 'error', - 'camelcase': 'warn', - 'new-cap': 'warn', - 'no-array-constructor': 'error', -}; - -const extraTsRules: Record = { - '@typescript-eslint/no-non-null-assertion': 'error', - '@typescript-eslint/no-dynamic-delete': 'error', - '@typescript-eslint/no-useless-empty-export': 'error', - '@typescript-eslint/consistent-type-imports': 'error', - '@typescript-eslint/unified-signatures': 'error', - '@typescript-eslint/no-extraneous-class': 'warn', - '@typescript-eslint/no-useless-constructor': 'warn', - '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', - '@typescript-eslint/no-invalid-void-type': 'warn', - '@typescript-eslint/prefer-literal-enum-member': 'warn', - '@typescript-eslint/prefer-enum-initializers': 'warn', - 'no-shadow': 'off', - '@typescript-eslint/no-shadow': 'error', - 'no-array-constructor': 'off', -}; +import { eslintExtraRules, eslintExtraTsRules } from '../rules/builtin-rules'; const TS_FILES = ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts']; @@ -126,8 +76,8 @@ export class ESLintAdapter implements LinterAdapter { ESLintAdapter.defaultConfig = [ js.configs.recommended, ...ts.configs.recommended, - { rules: extraRules }, - { files: TS_FILES, rules: extraTsRules }, + { rules: eslintExtraRules }, + { files: TS_FILES, rules: eslintExtraTsRules }, ]; } return ESLintAdapter.defaultConfig; diff --git a/src/adapters/sqlfluff.ts b/src/adapters/sqlfluff.ts index 2612cee..2da2d16 100644 --- a/src/adapters/sqlfluff.ts +++ b/src/adapters/sqlfluff.ts @@ -7,6 +7,7 @@ import type { LinterAdapter, AdapterResult, LinterDiagnostic, Severity } from '. import { getSqlFluffConfigFile, getSqlFluffDialect } from '../config'; import { t } from '../i18n/messages'; import staticRules from '../rules/static-rules.json'; +import { BUILTIN_SQLFLUFF_RULES, buildBuiltinSqlfluffConfig } from '../rules/builtin-rules'; const DIALECT_MAP: Record = { sql: 'oracle', @@ -20,19 +21,6 @@ const SUPPORTED_DIALECTS = [ 'soql', 'sparksql', 'sqlite', 'starrocks', 'teradata', 'trino', 'tsql', 'vertica', ]; -const BUILTIN_SQLFLUFF_RULES = - 'core,AM03,AM05,AM08,CV01,CV02,CV06,CV08,CV12,LT13,LT14,LT15,ST01,ST02,ST04,ST05,ST06,ST07,ST09,ST10,ST11,ST12,RF02,RF04,RF05,RF06'; - -function buildBuiltinConfig(dialect: string): string { - return `[sqlfluff] -rules = ${BUILTIN_SQLFLUFF_RULES} -dialect = ${dialect} -max_line_length = 80 -indent_unit = space -tab_space_size = 4 -`; -} - interface RuleEntry { id: string; description: string; tier?: string; } const tierMap = new Map(); @@ -156,7 +144,7 @@ export class SqlFluffAdapter implements LinterAdapter { } else if (hasProjectSqlfluffConfig(workingDir)) { } else { tempConfigPath = path.join(os.tmpdir(), `vscode-code-reviewer-sqlfluff-${Date.now()}.cfg`); - fs.writeFileSync(tempConfigPath, buildBuiltinConfig(cliDialect ?? fallbackDialect), 'utf-8'); + fs.writeFileSync(tempConfigPath, buildBuiltinSqlfluffConfig(cliDialect ?? fallbackDialect), 'utf-8'); configPath = tempConfigPath; } diff --git a/src/adapters/stylelint.ts b/src/adapters/stylelint.ts index 18a0bb6..ab20cc0 100644 --- a/src/adapters/stylelint.ts +++ b/src/adapters/stylelint.ts @@ -3,7 +3,7 @@ import * as fs from 'fs'; import * as path from 'path'; import type { LinterAdapter, AdapterResult, LinterDiagnostic, Severity } from './adapter'; import { getStylelintConfigPath } from '../config'; -import recommendedConfig from 'stylelint-config-recommended'; +import { stylelintExtraRules } from '../rules/builtin-rules'; const CONFIG_FILE_NAMES = [ '.stylelintrc', @@ -16,48 +16,18 @@ const CONFIG_FILE_NAMES = [ 'stylelint.config.cjs', ]; -const extraRules: Record = { - 'color-no-invalid-hex': true, - 'function-linear-gradient-no-nonstandard-direction': true, - 'function-no-unknown': true, - 'unit-no-unknown': true, - 'no-unknown-animations': true, - 'no-unknown-custom-media': true, - 'no-unknown-custom-properties': true, - - 'at-rule-no-vendor-prefix': true, - 'media-feature-name-no-vendor-prefix': true, - 'property-no-vendor-prefix': true, - 'selector-no-vendor-prefix': true, - 'value-no-vendor-prefix': true, - - 'color-hex-length': 'short', - 'color-function-notation': 'modern', - 'length-zero-no-unit': true, - 'selector-pseudo-element-colon-notation': 'double', - 'import-notation': 'string', - 'alpha-value-notation': 'number', - 'hue-degree-notation': 'angle', - 'keyframe-selector-notation': 'percentage', - - 'declaration-block-no-redundant-longhand-properties': true, - 'shorthand-property-no-redundant-values': true, - 'block-no-redundant-nested-style-rules': true, - - 'color-named': 'never', - 'font-family-name-quotes': 'always-where-required', - - 'number-max-precision': 4, - 'comment-whitespace-inside': 'always', -}; - -const DEFAULT_CONFIG: Record = { - ...(recommendedConfig as Record), - rules: { - ...((recommendedConfig as Record).rules as Record), - ...extraRules, - }, -}; +async function getDefaultConfig(): Promise> { + const mod = await import('stylelint-config-recommended'); + const recommendedConfig = (mod.default ?? mod) as Record; + const recommendedRules = (recommendedConfig.rules ?? {}) as Record; + return { + ...recommendedConfig, + rules: { + ...recommendedRules, + ...stylelintExtraRules, + }, + }; +} interface LinterOptions { code?: string; @@ -121,7 +91,7 @@ export class StylelintAdapter implements LinterAdapter { if (globalPath && globalPath.trim() !== '') { lintOptions.configFile = globalPath; } else if (!hasExternalConfig(workingDir)) { - lintOptions.config = DEFAULT_CONFIG; + lintOptions.config = await getDefaultConfig(); } const result = await stylelint.lint(lintOptions); diff --git a/src/i18n/messages.ts b/src/i18n/messages.ts index 75b9848..adb9fbc 100644 --- a/src/i18n/messages.ts +++ b/src/i18n/messages.ts @@ -193,29 +193,29 @@ const messages: Record> = { ja: 'クイックスタート', }, 'setup.gettingStarted': { - 'zh-CN': '三步启用代码审核', - en: '3 Steps to Enable Code Review', - ja: '3ステップでコードレビューを有効化', + 'zh-CN': '三步开始使用', + en: '3 Steps to Get Started', + ja: '3ステップで使い始める', }, 'setup.step1': { - 'zh-CN': '安装插件后,配置 AI 模型及 API Key,激活智能审核能力', - en: 'After installing, configure AI model and API Key to activate intelligent review', - ja: 'インストール後、AIモデルとAPIキーを設定してインテリジェントレビューを有効化', + 'zh-CN': '插件已内置 Linter 静态分析,开箱即用,也可在共通规则中配置项目/全局规则', + en: 'The extension includes built-in Linter static analysis, ready to use out of the box, and you can also configure project/global rules in Common Rules', + ja: '拡張機能にはLinter静的解析が組み込まれており、そのまま利用できるほか、共通ルールでプロジェクト/グローバルルールも設定できます', }, 'setup.step2': { - 'zh-CN': '启用 自定义规则,补充团队特有的编码规范', - en: 'Enable custom rules to add team-specific coding standards', - ja: 'カスタムルールを有効にしてチーム固有のコーディング規約を追加', + 'zh-CN': '在 自定义规则 标签页导入团队编码规范,增强审查(可选)', + en: 'Import team coding standards in the Custom Rules tab to enhance reviews (optional)', + ja: 'カスタムルールタブでチームのコーディング規約をインポート(任意)', }, 'setup.step3': { - 'zh-CN': '保存并测试连接,验证配置无误后即可触发审核', - en: 'Save and test connection, verify config then trigger review', - ja: '保存して接続テスト、設定を確認してレビューを開始', + 'zh-CN': '配置 AI 模型与 API Key保存并测试连接,启用 AI 深度审查', + en: 'Configure AI model & API Key and save & test the connection to enable deep AI review', + ja: 'AIモデルとAPIキーを設定し保存して接続テスト、AI詳細レビューを有効化', }, 'setup.step3Hint': { - 'zh-CN': '按 Ctrl + Shift + R 快捷键触发审核,结果实时显示在 审核结果报告页面中', - en: 'Press Ctrl + Shift + R to trigger review, results appear in the Review Report panel', - ja: 'Ctrl + Shift + R でレビューを実行、結果はレビューレポートに表示', + 'zh-CN': '按 Ctrl + Shift + R 触发完整审核,结果实时显示在审核结果报告页面', + en: 'Press Ctrl + Shift + R to run a full review, results appear in the Review Report panel', + ja: 'Ctrl + Shift + R で完全なレビューを実行、結果はレビューレポートに表示', }, 'setup.aiConnectionConfig': { 'zh-CN': 'AI 连接配置', @@ -407,6 +407,11 @@ const messages: Record> = { en: 'Auto-selects config by priority: Built-in < Global < Project', ja: '優先順位に従って自動選択: 組み込み < グローバル < プロジェクト', }, + 'setup.adapter.modeLegendHelp': { + 'zh-CN': '三种配置来源的含义:\n· 内置规则:插件自带的规则集,开箱即用,无需配置\n· 全局配置:在 VS Code 设置中指定的文件路径,对所有项目生效\n· 项目配置:项目根目录下的配置文件,仅对当前项目生效\n\n优先级:内置 < 全局 < 项目,插件自动选择优先级最高的可用配置', + en: 'What the three config sources mean:\n· Built-in rules: bundled with the extension, work out of the box\n· Global config: a file path set in VS Code settings, applies to all projects\n· Project config: a config file in the project root, applies to the current project only\n\nPriority: Built-in < Global < Project; the highest-priority available source is used', + ja: '3つの設定ソースの意味:\n· 組み込みルール:拡張機能に同梱のルール、設定不要ですぐに使用可\n· グローバル設定:VS Code設定で指定したファイルパス、全プロジェクトに適用\n· プロジェクト設定:プロジェクト直下の設定ファイル、現在のプロジェクトのみに適用\n\n優先度:組み込み < グローバル < プロジェクト、利用可能な中で最優先のものを自動選択', + }, 'setup.adapter.configYes': { 'zh-CN': '已配置', en: 'Configured', @@ -507,40 +512,40 @@ const messages: Record> = { en: 'Java (including Java code in JSP, e.g. <% ... %>)', ja: 'Java(JSP内のJavaコードを含む、例: <% ... %>)', }, - 'setup.adapter.pmdGuide': { - 'zh-CN': '需要 Java 运行环境;项目根目录创建 ruleset.xml 或在设置中配置 pmd.rulesetPath', - en: 'Requires Java runtime; create ruleset.xml in project root or set pmd.rulesetPath in settings', - ja: 'Java実行環境が必要。プロジェクトルートにruleset.xmlを作成するか、設定でpmd.rulesetPathを設定してください', + 'setup.adapter.pmdHelp': { + 'zh-CN': 'PMD 由 Java 编写,需先安装 Java 运行环境(JDK 8+),否则无法执行。\n\n插件已内置一套 PMD 规则集(7 类 274 条),安装 Java 后即可开箱使用,无需额外配置。\n点击「创建项目配置」,会在项目根目录生成 ruleset.xml,内容与插件内置规则集完全一致(含每类规则的说明注释),方便查看与调整审查范围。\n保存文件即自动生效,无需重启。\n\n若多个项目共用同一份规则,可点「修改全局设置」,在 pmd.rulesetPath 中填写该文件路径', + en: 'PMD is written in Java, so a Java runtime (JDK 8+) must be installed first.\n\nThe extension ships a built-in PMD ruleset (7 categories, 274 rules), usable right after installing Java.\nClick "Create Project Config" to generate ruleset.xml in the project root, identical to the built-in ruleset (including per-category comment descriptions) for review and adjustment.\nChanges take effect on save, no restart needed.\n\nTo share one ruleset across projects, click "Modify Global Settings" and set pmd.rulesetPath', + ja: 'PMDはJava製のため、まずJava実行環境(JDK 8+)のインストールが必要です。\n\n拡張機能にはPMDルールセット(7カテゴリ・274ルール)が同梱されており、Java導入後すぐに利用できます。\n「プロジェクト設定を作成」をクリックすると、プロジェクト直下にruleset.xmlが生成されます。内容は組み込みルールセットと完全一致し(カテゴリごとの説明コメント付き)、確認・調整が可能です。\n保存後すぐに反映され、再起動は不要です。\n\n複数プロジェクトで同じルールを共有する場合、「グローバル設定を変更」でpmd.rulesetPathを設定してください', }, 'setup.adapter.sqlLanguages': { 'zh-CN': 'SQL', en: 'SQL', ja: 'SQL', }, - 'setup.adapter.sqlGuide': { - 'zh-CN': '需要 Python 环境和 sqlfluff;运行 pip install sqlfluff,项目根目录创建 .sqlfluff', - en: 'Requires Python and sqlfluff; run pip install sqlfluff, create .sqlfluff in project root', - ja: 'Python環境とsqlfluffが必要。pip install sqlfluff を実行し、プロジェクトルートに.sqlfluffを作成してください', + 'setup.adapter.sqlHelp': { + 'zh-CN': 'SQLFluff 是 Python 命令,需先安装 Python 环境与 sqlfluff(pip install sqlfluff),否则无法执行。\n\n插件已内置一套 SQLFluff 精选规则(26 项),安装后即可开箱使用,无需额外配置。\n点击「创建项目配置」,会在项目根目录生成 .sqlfluff,内容与插件内置配置一致(含每条规则的说明注释,以及 rules = all 的启用提示),方便查看与调整。\n保存文件即自动生效,无需重启。\n\n若多个项目共用同一份配置,可点「修改全局设置」填写全局路径', + en: 'SQLFluff is a Python command; install Python and sqlfluff (pip install sqlfluff) first.\n\nThe extension ships built-in SQLFluff curated rules (26 items), usable right after install.\nClick "Create Project Config" to generate .sqlfluff in the project root, identical to the built-in config (including per-rule comment descriptions and a rules = all hint) for review and adjustment.\nChanges take effect on save, no restart needed.\n\nTo share one config across projects, click "Modify Global Settings" and set the global path', + ja: 'SQLFluffはPython製コマンドのため、まずPython環境とsqlfluff(pip install sqlfluff)が必要です。\n\n拡張機能にはSQLFluff精選ルール(26項目)が同梱されており、インストール後すぐに利用できます。\n「プロジェクト設定を作成」をクリックすると、プロジェクト直下に.sqlfluffが生成されます。内容は組み込み設定と一致し(ルールごとの説明コメントとrules = allのヒント付き)、確認・調整が可能です。\n保存後すぐに反映され、再起動は不要です。\n\n複数プロジェクトで同じ設定を共有する場合、「グローバル設定を変更」でパスを設定してください', }, 'setup.adapter.eslintLanguages': { 'zh-CN': 'JS, TS, JSX, TSX(含 JSP 中的 JavaScript 代码,例如