XPerf
语言JavaScript

jsperf-lint

这篇还没有中文,下面是英文原文。

Zero-dependency Node / pure JS heuristics over .js / .mjs / .cjs / .ts / .tsx (line windows; not a full parser).

Rules (v0)

IDappengineallSummary
jp-alloc-hotoffofferror.push({ / non-empty binders / clear new / Object.create (G3 OK-idiom → off)
jp-console-hotoffhinterrorconsole.* — remove or gate; cost adds up even off a hot path
jp-delete-shapeoffofferrordelete on a plain member / computed key (G3 OK-idiom on Node → off)
jp-delete-array-holeerrorerrorerrordelete arr[i] — holey arrays (G4; narrowed index heuristic)
jp-map-filter-chainhinthinterrorchained .map/.filter/.flatMap; cost grows with length
jp-regex-ctor-hothinthinterrornew RegExp when the pattern is stable (not a loop binding)
jp-json-hothinthinterrorJSON.parse / JSON.stringify when the text is stable (not a loop binding)
jp-dom-query-hothinthinterrorquerySelector / getElementById when the selector is stable (not a loop binding)
jp-intl-datetimeformat-hothinthinterrornew Intl.DateTimeFormat when locale/options are stable (not a loop binding)
jp-readfile-sync-hothinthinterrorreadFileSync when the path is stable (not a loop binding)
jp-clock-hothinthinterrorDate.now / performance.now when a fresh time is not required each call
jp-query-all-starhinthinterrorquerySelectorAll('*') full-tree walk (also sees scripts inside executeJavaScript templates)
jp-execjs-in-timerhinthinterrorbuilt executeJavaScript / evaluate script re-sent from setInterval / setTimeout
jp-clone-per-eventhinthinterrorstructuredClone / JSON.parse(JSON.stringify(...)) on an event-like payload

Scope (orthogonal to presets): default --scope all reports the smells above everywhere — extreme performance stacks care about drip-by-drip cost, not only loops. Opt in to --scope loop for the former in-loop-only matching (loop headers and nested function/arrow bodies defined inside a loop stay out of that narrower match). Loop-binding arguments still suppress hoist-style rules when the call sits in a loop. Path skips include e2e/, playwright-report/, and *.test.* / *.spec.*. Template-string scripts passed to executeJavaScript / evaluate are scanned.

Finding messages describe the smell and invite a fix; they do not tell readers or models that non-loop sites are out of scope.

Does not detect IC polymorphism, elements-kind at runtime, or deopts — see guide §4.5.

Usage

node jsperf-lint/bin/jsperf-lint.js PATH... [--format text|json]
node jsperf-lint/bin/jsperf-lint.js PATH... --preset app|engine|all
node jsperf-lint/bin/jsperf-lint.js PATH... --scope all|loop
node jsperf-lint/bin/jsperf-lint.js PATH... --severity jp-alloc-hot=error --severity jp-map-filter-chain=off
node jsperf-lint/bin/jsperf-lint.js PATH... --strict-hints
node jsperf-lint/bin/jsperf-lint.js PATH... --no-fail

Default --preset app. Default --scope all. --severity overrides the preset. Text: path:line: error jp-delete-array-hole: …. JSON includes "severity". Exit 1 only for error findings (hints print but exit 0 unless --strict-hints).

Self-lint (mandatory dogfood)

cd packages/jsperf
npm run self-lint   # lints bin/ + lib/; must exit 0 with 0 findings
npm test            # fixtures + preset smoke

Dogfood corpora: ../docs/corpus.md (trees are not shipped; clone locally). Corpus-ready: yes (2026-10-22 redesign).

Evidence

Measured gates: ../evidence/jp-*.{md,jsonl} (jp-delete-array-hole → error; jp-delete-shape demoted off after G3 despite JSONL; jp-map-filter-chain has JSONL and stays hint because a short array is a different cost). jp-string-plus-in-loop is removed. jp-dom-query-hot and jp-intl-datetimeformat-hot have JSONL and stay hint (not corpus-triaged; outside the 2026-10-22 verdict). Policy: ../docs/evidence-policy.md.