Skip to content

feat: surface core executionErrors and exit(1) (#96) - #101

Merged
luojiyin1987 merged 5 commits into
masterfrom
feat/execution-error-exit
Jul 13, 2026
Merged

feat: surface core executionErrors and exit(1) (#96)#101
luojiyin1987 merged 5 commits into
masterfrom
feat/execution-error-exit

Conversation

@luojiyin1987

Copy link
Copy Markdown
Contributor

适配 core #185(已随 @lint-md/core 2.1.5 发布)

core 2.1.5 把规则执行失败从"隐式 console.error / 抛错"改为结构化返回 LintMdResult.executionErrors: RuleExecutionError[]。CLI 之前全链路漏掉它,导致有规则执行错误时仍 exit(0)、CI 静默通过

改动

文件 修改
src/types.ts BatchLintItem.executionErrors?: RuleExecutionError[]
src/utils/lint-worker.ts 回传 executionErrors: result.executionErrors
src/utils/batch-lint.ts keepLintItem 加 `
src/utils/report-execution-errors.ts 新:getExecutionErrorWarnings() / hasExecutionErrors()
src/lint-md.ts 4 处串入(stdin lint/fix、file lint/fix)+ exit(1) 判定

关键决策(与 issue 评审一致)

  1. 不被 --suppress-warnings 抑制:rule 执行失败是硬错误,不是文档级 lint 警告,CLI 必须 stderr + exit(1)。
  2. 独立 stderr 通道(方案 B):不混入 getReportData()errorCount / 表格,保持"X problems"语义不变。
  3. 文件 --fix 在完成 safeWriteFile 后输出 errors 再 exit(1):保留其他正常规则产生的修复,同时让 CI 明确失败。
  4. 基于 feat: 消费 fix convergence 状态并对未完全收敛给出警告 (#182 适配) #98{ allResults, actionableResults } 结构:helper 入参 BatchLintItem[],调用方按路径传 actionableResults / 临时 item;与 report-incomplete-fixes.ts 共存、不互相调用。
  5. 不去重:fix 模式 errors 汇总每一轮,含 round;严格 1:1 输出,丢"同一规则不同轮失败"的核心诊断。

文案

[lint-md] <path>: <ruleName> failed in <phase> (round <n>, node <nodeType>): <message>
  • nodeType 缺失 → 省略该片段(不留空逗号)
  • path / ruleName / nodeType / message 全部 sanitizeTerminalText()

验证

  • npm run build
  • npm test:17 套件 / 146 用例全过(较 feat: surface fix convergence warnings (#98) #100 基线净增 12 个)
  • npm run lint(tsc --noEmit + prettier --check)✅
  • report-execution-errors.ts 覆盖率 100%
  • 非 E2E:规则执行错误需要函数(JSON 不能序列化),全部用构造 BatchLintItem / mock lintMarkdown 单测

文件

  • 新增:src/utils/report-execution-errors.ts__tests__/lint-worker.spec.ts__tests__/report-execution-errors.spec.ts
  • 修改:src/types.tssrc/utils/lint-worker.tssrc/utils/batch-lint.tssrc/lint-md.ts__tests__/keep-lint-item.spec.ts

Closes #96

Adapt @lint-md/core 2.1.5 (core #185), which returns rule execution
errors as a structured list (RuleExecutionError[]) instead of throwing
or logging implicitly. The CLI previously dropped them, so a crashing
rule produced a silent exit(0) and let CI pass.

- types.ts: BatchLintItem.executionErrors?: RuleExecutionError[]
- lint-worker.ts: pass result.executionErrors through to the caller
- batch-lint.ts: keepLintItem now also retains items that carry
  executionErrors, so the warning + exit(1) have a target
- report-execution-errors.ts (new): getExecutionErrorWarnings() emits
  one stderr line per error (not deduped across rounds/phases) and
  hasExecutionErrors() for the exit gate. Path / ruleName / nodeType /
  message are all sanitized. nodeType is omitted when absent.
- lint-md.ts: surface executionErrors on every path (stdin lint/fix,
  file lint/fix) via the dedicated stderr channel, after the report /
  fixes are written. Exit 1 is taken regardless of --suppress-warnings,
  since a rule failure is a hard error, not a document-level lint
  finding.

Built on the #98 { allResults, actionableResults } result shape; the
helper mirrors report-incomplete-fixes.ts.

Tests: lint-worker mock passthrough, keepLintItem retention, and
report-execution-errors behaviour (multi-error, missing nodeType,
no dedupe, sanitization).
…truncated pipe output

P1: the four execution-error exit points previously called process.exit(1)
right after writing stdout (stdin --fix) / stderr diagnostics. On POSIX
pipes, process.exit() can terminate before stdout/stderr flush, truncating
the fixed markdown in  and
dropping the error diagnostics. Switch to process.exitCode = 1 so the
process exits naturally and all buffered I/O completes (Node docs: set
process.exitCode rather than calling process.exit()).

P2: extract the shared exit-decision into emitExecutionErrorsAndSetExitCode()
(diagnostics -> stderr, sets process.exitCode = 1, returns whether errors
existed) and route all four entry points (stdin lint/fix, file lint/fix)
through it. Add direct unit tests for the contract: writes diagnostics,
sets exitCode = 1, no-op when empty, idempotent at 1. The helper keeps the
--suppress-warnings bypass and per-round/phase, no-dedupe, sanitized output
from reportExecutionErrors().

Verified: npm run build / npm test (152 pass, +6) / npm run lint all green.
…d early-return on lint failure

Store emitExecutionErrorsAndSetExitCode()'s return value as hasRuleFailures
and reuse it in the existing exit decision, instead of re-scanning items
with hasExecutionErrors() and re-assigning process.exitCode. Add an early
return once the exit code is set so stdin lint and file lint no longer
emit the trailing 'Done in ...' timing line after a failure, matching the
previous process.exit(1) behaviour.
…p trailing Done line

Save emitExecutionErrorsAndSetExitCode()'s return value and return after
the dev metrics block when a rule failure is present, so file --fix no
longer prints the trailing 'Done in …' timing line on failure — matching
the previous process.exit(1) behaviour. Writes, stderr diagnostics, exit
code and IO flushing are unaffected.
@luojiyin1987
luojiyin1987 merged commit e863059 into master Jul 13, 2026
5 checks passed
@luojiyin1987
luojiyin1987 deleted the feat/execution-error-exit branch July 13, 2026 13:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 透传 core executionErrors 并在有规则执行错误时 exit(1) (#185 适配)

1 participant