Skip to content

Verification system

Verification in lh-ui is not a formality — every check guards a class of regression that has actually happened before. This page explains item by item what is checked and how to read failure output.

终端窗口
pnpm verify:dts

What it checks: the compiled .d.ts files in es/ / lib/ are consistent with the source type declarations, and the files pointed to by the exports keys really exist.

Typical failures:

  • The source types were changed but pnpm build was not re-run (the output is stale).
  • sync:exports generated an exports key, but the corresponding file was never built (a misassembled path).
  • import type was written as import, leaving an extra copy of runtime code in the output.

Fix: re-run pnpm build in full, then pnpm verify:dts.

终端窗口
pnpm verify:styles

Script: scripts/verify-styles.mjs. The six checks run in order; the first failure exits.

A blacklist check: any of Element Plus’s 9 legacy color values (#409eff, #e6a23c, #f56c6c, #67c23a, #909399, #606266, #303133, #ebeef5, #c0c4cc) appearing in the scss files under styles/ fails the check. This is a regression guard from the D2 cleanup, keeping the old palette from creeping back — every lh-ui color must come from the token definitions in styles/theme/common/variables.scss.

Checks that every var(--lh-*) in component scss carries a fallback value:

// ✅ passes
color: var(--lh-text-primary, rgba(0, 0, 0, 0.88));
// ❌ fails
color: var(--lh-text-primary);

Why: when a token is missing (the user imports only part of the styles, or an older version lacks the token), a var() without a fallback resolves to an invalid value and the declaration is dropped — text becomes default black, borders vanish. A fallback guarantees “graceful degradation”.

Checks that component scss imports the token layer through @use rather than scattering hex literals:

// ✅ passes
@use 'theme/common/var' as *;
.lh-card { background: var(--lh-bg-color, #fff); }
// ❌ fails
.lh-card { background: #ffffff; }

Exemption: hex is allowed inside a var() fallback (that is what the fallback is for). Failure output lists the file and line number.

Checks that styles/index.scss (the full aggregation) matches “the component list obtained by scanning styles/*/index.scss”. styles/index.scss is generated by scripts/build-styles.mjs — do not hand-edit it; failing to re-run the generator after adding a component directory makes the two disagree.

Checks component scss for bare -enter-from / -leave-from literals:

// ❌ fails: hand-written duplicate transition class names
.lh-dialog-fade-enter-from { opacity: 0; }
// ✅ passes: using the mixin
@include lh-enter-both('.lh-dialog-fade') { opacity: 0; }

Why: Vue2 uses -enter, Vue3 / React use -enter-from; hand-writing both inevitably misses one port (see transition-mixins). 16 components once each wrote both sets, and fixing one place missed another.

Checks that tokens in var.scss whose value looks like #{$lh-foo} (baked into a light value at compile time) have a matching override in var-dark.scss.

This is the most critical check. Gaps of this kind once left menu / table / timeline text in dark mode using the light values, rendering it invisible (black text on a black background). See the compile-time baking trap in Dark mode.

Note: there was once a false-positive fix — an off-by-one index in verify:styles falsely flagged a no-fallback exemption line (commit 791ff32). If a failure message looks unreasonable (an exempt line still being reported), first confirm whether it is this known issue.

终端窗口
pnpm build # ← must run first; it reads the es/ output
pnpm verify:theme

Script: scripts/verify-theme-contrast.mjs.

What it checks: across the five built-in themes (and their dark variants), every “text / background” pair has a WCAG contrast ratio ≥ 4.5:1 (level AA).

Why it must read the es/ output: a preset theme’s vars only reach their final form after the build (the derivation results of buildPresetDarkVars are also in the output).

Typical failures:

  • A new preset’s light primary was not given --lh-color-on-primary (white text on a light primary only reaches 3.x:1).
  • The selected-state text color (--lh-selected-color) has insufficient contrast against the hover background.

Fix: darken / lighten the relevant colors until the contrast passes. getContrastRatio() and getRelativeLuminance() (exported from src/theme/themes/index.ts) can compute it locally, so you need not run the full gate every time.

CI does not run this check — it needs a build first and is not cheap. Guard it manually before a local release.

终端窗口
pnpm verify:versions

Script: scripts/check-version-sync.mjs.

What it checks: the @lh-ui/share dependency range in lh-ui-plus’s / lh-ui-vue2’s package.json covers share’s current version.

Current status: passes while share is 0.3.0. It will fail after 0.4.0 is published — because both UI libraries’ dependency ranges are still ^0.3.0. This is expected behavior: per red line 5 (share is released first, the UI libraries follow), after a release you go to each sibling repo and run pnpm up @lh-ui/share@0.4.0 plus a regression pass.

Command Scan scope
pnpm lint src / tests / scripts / vite.config.ts / vitest.config.ts
pnpm typecheck src/**/* (the include of tsconfig.json)

docs/ is not in the scan scope — the docs site is a standalone Astro project whose quality is guarded by pnpm docs:build. Likewise styles/ is not in the typecheck scope (SCSS is handled by verify:styles).

lint’s framework-dependency gate: CI greps src/ for import 'vue' / import 'react' / @dcloudio/* — this is red line 1; core must be framework-agnostic.

终端窗口
pnpm test

tests/unit/ is the single copy of core unit tests (red line 3): all three libraries share the results, and UI libraries must not copy them. Testing pure functions needs no component rendering — construct literal rects, assert the output, and all four ports share the same assertions.

Key test files:

File Coverage
theme.spec.ts preset consistency (two-source sync between JS vars and SCSS variables)
core/*.spec.ts each core component’s logic
shared/*.spec.ts shared kernel algorithms (popper / keyboard / virtual, etc.)

The complete checklist before a release:

终端窗口
pnpm install
pnpm lint
pnpm typecheck
pnpm test
pnpm build # note that sync:exports modifies package.json
pnpm verify:dts
pnpm verify:styles # the six checks
pnpm verify:theme # requires a build first
pnpm verify:versions # turns into downstream work after a release
pnpm docs:build # the docs site

prepublishOnly (triggered by pnpm release) includes only build + verify:dts + verify:styles. So test / lint / typecheck / verify:theme must be run explicitly by hand.

Copyright © 2024-2026 FengMo WenXue