2026 Uplift
评估
- 难度
- 5/5
- 预计耗时
- 一周以上
- 新手友好度
- 25/100
- Issue 类型
- 重构
- 描述清晰度
- 基本清楚
- 活跃度
- 活跃
- 技术栈
- github-actions, javascript, typescript
- 领域
- build-system, devtools, release
调研方向
Read the Phase 2 checklist and the linked patterns from FormidableLabs/victory and nearform/llm-splitter. Start with the oxlint + oxfmt config PR, then review its format and lint churn plans, preserving the stated ES5 and upstream comment constraints in index.js. The tracking issue is complete when the remaining phases and merge steps are done; it spans tooling, types, packaging, and release work, so it is not a bounded first contribution.
由索引模型根据 Issue 内容生成。
描述
Living tracking issue, modelled on FormidableLabs/victory#3093 (and nearform/llm-splitter for the single-package, JSDoc-typed setup), but much smaller. We'll keep editing it as we go, and record what changed in the Decision log at the bottom.
Status (2026-11-05): Phase 1 is underway on the integration branch chore/2026-uplift. Merged: #141 (pnpm 12, Node 26, vendored test cases, Codecov and AppVeyor removed, mocha 11 so test-node runs on Node 26). Also on the branch: changesets v3 + OIDC release.yml with canary (65c9439, pushed without a PR), publishConfig.provenance dropped, CONTRIBUTING release section. npm-publish environment and npm trusted publisher are set up, and a canary from the branch published 3.2.3-canary-20261105232110 to next through OIDC with provenance (run). Phase 1 is done apart from deleting the NPM_TOKEN secret. Next: Phase 2, starting with the oxlint + oxfmt config PR.
Why
- Nothing has been released since 3.2.2 (Mar 2023).
release.ymlpublishes withNPM_TOKENand gives its one job broad write permissions; no OIDC. - The dev stack is from 2020: yarn 1,
builder, mocha + nyc, Karma + webpack + Babel + core-js (for IE11), ESLint 8, TS 4.9, Node 18 in CI, Codecov. - Correctness tests mostly come from a git dependency,
fast-deep-equal-git(epoberezkin/fast-deep-equal#v3.1.3): 62 generic + 49 ES6 suites from itsspec/. Upstream hasn't released since 2020. - Open dev-only security alerts (#70) and three stale Dependabot PRs (#122, #124, #128) all come from the Karma/webpack tree.
- New bug: #139, Temporal values throw (
valueOf). It's the first source change after the uplift, and the first release from the new pipeline.
By the numbers (Nov 2026)
- ~107M downloads/month. Last week by version: 3.2.2 ~27.5M, 2.0.4 ~6.1M, 3.2.0 ~1.45M. Most users are pinned through dependents' ranges (
^3,^2). - Published surface:
index.js(CJS,module.exports = isEqual, ES5) andindex.d.ts(export =). Noexportsmap, no ESM, nosideEffects. There's no UMD bundle to drop, unlike victory. - Quick probe (2026-11-05) of current
index.js:- React 19.3.1 (dev and prod): elements compare correctly. In dev,
_owneris enumerable and still skipped; the_debug*props are non-enumerable. - Preact 10.29 / 11.1 with
preact/compat: correct. - Plain
preacth()vnodes with compat not loaded comparefalse(no$$typeof, so__vis traversed). This is pre-existing and the same in 10 and 11; only compat is documented. A later source item, not uplift work.
- React 19.3.1 (dev and prod): elements compare correctly. In dev,
- JSDoc spike (TS 7.0.2,
allowJs+checkJs+strict,emitDeclarationOnly): a 5-line JSDoc block onisEqualemits exactly today'sindex.d.ts(declare function isEqual<B>(a: any, b: B): a is B; export = isEqual;).strictreports 7 errors insideequal()(implicitanyparams,ArrayBufferViewindexing,catch (error)asunknown). All can be fixed with JSDoc annotations or casts, with no runtime change.
Principles
- Infra first, source last. Mechanical source churn (oxfmt, lint fixes, JSDoc types) lands in its own PR with no behavior change, and goes into
.git-blame-ignore-revs. - The uplift doesn't change what users get. Same files, same
module.exports = isEqual, same ES5 code, same declarations. Only formatting and comments may change inindex.js, andpack:checkproves the rest (see Phase 2). ESM,exportsand a modern target wait for 4.0.0. - Types come from JSDoc, as in llm-splitter. Source stays plain JS with no build step for the code.
tsctype-checks it (checkJs) and, from 4.0, generates the.d.ts. - Plain
package.jsonscripts. No task runner, since this is a single package. - Crib from victory and llm-splitter where they fit a one-file library.
Versioning (decided 2026-11-05)
- Uplift →
masterwith no published change. No changeset is needed for the infra PRs; there's nothing to release from them alone. - #139 (Temporal) → 3.x (patch or minor), released from the new OIDC pipeline. It reaches every
^3user, and it's the pipeline's first real run, like victory's interim 37.4.0. #137 (sideEffects: false) can ride along; it's a safe manifest addition. - 4.0.0 later: ESM + CJS,
exportsmap, modern target (drop ES5/IE),.d.tsgenerated from JSDoc. Its own design section when we get there (see "4.0.0" below).
Branching and merge strategy
- Default branch stays
master(decided 2026-11-05). - Integration branch:
chore/2026-uplift, cut frommaster. Small PRs into it (squash is fine). CI runs onmaster+chore/2026-uplift*. - Final merge: one PR into
masterwith a merge commit, which keeps the blame-ignore SHAs valid. Repo settings to change first (same traps as victory):- Allow merge commits (currently off:
mergeCommitAllowed: false). - "Automatically delete head branches" is on. Turn it off, or protect
chore/2026-uplift*with a ruleset.
- Allow merge commits (currently off:
- Release gate for the merge: green CI, and
pack:checkshows the packed tarball matches 3.2.2 (see Phase 2). A canary tonextfrom the branch proves OIDC before #139 needs it.
Phase 1: Package manager and release pipeline (OIDC)
- pnpm 12 for development (#141) (
packageManager,pnpm-workspace.yamlsettings,minimumReleaseAge: 10080,allowBuilds). Removeyarn.lock. README install instructions say onlynpm install react-fast-compare; CONTRIBUTING says pnpm. - Repo files, as in victory (
.nvmrc,.editorconfig,.gitignoredone in #141;.vscode/moved to Phase 2 with oxc):.nvmrc→26, used by CI vianode-version-fileand documented in CONTRIBUTING. Still noenginesfield..editorconfig(2 spaces, LF, final newline; Markdown keeps trailing whitespace)..vscode/extensions.json(oxc.oxc-vscode) and.vscode/settings.json(oxc as formatter). Narrow the.vscode/gitignore entry to allow these two files.- Later phases add
.oxlintrc.json,.oxfmtrc.json,.git-blame-ignore-revs,.ncurc.cjs(dep:check),.github/zizmor.ymland.github/pinact.yml, plusAGENTS.md/CLAUDE.md. .gitignore: trim the 2017 template (bower, grunt, jspm, lib-cov, …).pnpm-lock.yamlis committed;package-lock.jsonandyarn.lockare ignored.
- changesets v3 + official
@changesets/changelog-github(disableThanks), replacing the deprecated@svitejs/changesets-changelog-github-compact.snapshot.useCalculatedVersionfor canaries.baseBranch: master. -
release.ymlonchangesets/actionv2 split jobs (llm-splitter's single-package version is the closest template; victory's adds pnpm and a canary): select-mode → version | pack → publish.permissions: {}with per-job grants,id-token: writeonly on publish/canary,package-manager-cache: false,persist-credentials: false. Onev{version}tag +gh release create --generate-notes.- Canary via
workflow_dispatchin the same file →X.Y.Z-canary-<datetime>onnext.
- Canary via
- Drop
publishConfig.provenance(automatic under trusted publishing).
npm and GitHub setup (manual, needs a maintainer)
One package, so this is done by hand in the npm and GitHub UIs (no npm-trust.sh like victory's).
- GitHub: create the
npm-publishenvironment (Settings → Environments), deployment branchesmaster+chore/2026-uplift. Required reviewers optional. NotProduction, to keep it separate from any deployment environments (victory decision log, 2026-10-26). - npm: on npmjs.com →
react-fast-compare→ Settings → Trusted Publisher → GitHub Actions: organizationFormidableLabs, repositoryreact-fast-compare, workflowrelease.yml, environmentnpm-publish. Renaming the workflow file or the environment breaks publishing until this is updated. - Validate: canary from the integration branch (
gh workflow run release.yml --ref chore/2026-uplift) publishesX.Y.Z-canary-<datetime>tonextwith provenance. It needs at least one pending changeset; use a throwaway one and delete it afterwards. Done 2026-11-05:3.2.3-canary-20261105232110, published by[email protected], SLSA v1 provenance forrelease.ymlonchore/2026-uplift. - npm → Publishing access → "Require two-factor authentication and disallow tokens" (done 2026-11-05, after the canary proved OIDC; earlier than planned).
- Delete the
NPM_TOKENrepo secret (unusable now that tokens are disallowed). - Before the final merge: allow merge commits, and turn off "Automatically delete head branches" (or protect
chore/2026-uplift*); see Branching. - After the final merge: remove
chore/2026-upliftfrom thenpm-publishdeployment branches and delete the branch.
Phase 2: Tooling
- oxlint + oxfmt replace ESLint 8 (+
@typescript-eslint/parser,eslint-plugin-react). Exact-pinned (0.x). The config PR also adds.vscode/extensions.json+settings.json(oxc) and narrows the.vscode/gitignore entry. Config PR, then a format-churn PR, then a lint-churn PR, with each churn SHA in.git-blame-ignore-revs.index.jsstays ES5 in 3.x: nono-var/prefer-const/ES2015 autofixes on it (test and tooling files can be modern).index.js'sSTART:/END: fast-deep-equalcomment blocks must survive the reformat; they're how upstream diffs are tracked (CONTRIBUTING).
- TypeScript 7 + JSDoc (llm-splitter pattern):
tsconfig.json:allowJs,checkJs,strict,noEmit,module/moduleResolution: nodenext,libwithdom(forElement). It currently has ESLint keys (env,parserOptions) in it; those go.- JSDoc on
isEqual(@template B,@param {any} a,@param {B} b,@returns {a is B}), plus annotations inequal()for the 7stricterrors. Its own PR, no runtime change. - Keep the hand-written
index.d.tsas the published file in 3.x. Atypes:checkstep emits declarations from JSDoc to a temp dir and fails if they differ fromindex.d.ts, so the two can't drift. In 4.0 the generated file replaces it. - Type probe (llm-splitter's
type-probeidea, but wired into CI since it's cheap here): a small.tsfixture compiled against the packed tarball undernode10,node16/nodenextandbundlerresolution. It coversReact.memo(C, isEqual),shouldComponentUpdate,useSelector-style(a, b) => booleancallbacks, thea is Bnarrowing, andimport isEqual fromwith and withoutesModuleInterop, with@ts-expect-errornegatives. Replacestest-ts-defs/test-ts-usageand thereact-redux/reduxdeps.
-
pack:check(simplified from victory's): onpnpm packoutput:- publint + attw.
- Isolated
require()andimport():require('react-fast-compare')(a, b)must stay callable, andimport isEqual frommust work in Node ESM. - Compatibility with 3.2.2: same file list and manifest fields (
main,types/typings,files), identicalindex.d.ts, andindex.jsminified byte-identical to 3.2.2's, so formatting, comments and JSDoc are the only changes. Dropped once 4.0 work starts.
- Size report, replacing
terser | gzip | wc -cand the hand-edited README badge: a small script (oxc-minify or terser, min + gzip) printed locally and in CI's job summary. Reported, not gated. - Remove cruft (
andcodecov+ README badgeAppVeyor/IE11 Karma config and referencesdone in #141):builder,nyc,sinon(usevi.spyOn),jsdom-global, Babel, core-js, webpack, Karma and its launchers,karma.conf.ie.js,test/mocha.opts,.eslintrc.yml/.eslintignore. - Minimal dev dependencies. Keep only what a script, test or CI step strictly needs; audit with knip at the end of each phase.
buildergoes first. - Small script surface, like victory. The entry points are
check,formatandtest:check: everything CI gates on, run in sequence (format, lint, types, test,pack:check).format/format:fix: oxfmt check / write.test: Vitest.- Only the pieces
checkcalls stay as scripts (lint/lint:fix,types:check,pack:check), plusdep:check,bench,sizeandchangeset. The rest go (eslint,tslint,test-*,compress,size-min-gz,preversion,benchmark).
-
AGENTS.md(+CLAUDE.mdpointer), as in victory and llm-splitter. It should cover: ES5 + no published change in 3.x, JSDoc types, the upstream comment blocks, changeset rules. -
dep:check(npm-check-updates, 7-day cooldown) and a documented manual update routine. No Renovate/Dependabot version PRs; turn on Dependabot security alerts after the final merge. Close #70.
Phase 3: Tests
One set of spec files, run in Node and in real browsers.
- Vitest 5 with two projects (decided 2026-11-05).
node: plain Node;jsdomonly for the specs that need a DOM.browser: Vitest browser mode + Playwright, Chromium, Firefox and WebKit headless. This replaces Karma and closes #12 (Sauce Labs).
- Vendor the upstream test cases (#141; pulled forward: pnpm packs git dependencies, which honors upstream's
fileslist, sospec/wasn't installed): copy[email protected]'sspec/tests.jsandspec/es6tests.jsintotest/fixtures/with its MIT notice, and drop thefast-deep-equal-gitgit dependency. They're frozen upstream anyway, and #139/#50 will add our own cases next to them. - Drop comparative tests and deps:
lodash,nano-equal,shallow-equal-fuzzy. - React tests on React 19 and a CI job on React 18.0.0, plus
preact/compat10 and 11. Replacereact-test-renderer(deprecated in 19) withreact-dom(renderToString/createRoot). Keep React 16/17 out of CI. The library doesn't depend on React, and its React handling ($$typeof+_owner) is the same shape across versions. - Coverage via
@vitest/coverage-v8in the CI job summary (no Codecov). - CI: ubuntu with Node
.nvmrc+ browsers; keep windows for the Node project only. zizmor in CI, hybrid action pinning (pinact),permissions: {}. Build-free, so the CI step that matters most besides tests ispack:check.
Phase 4: Benchmarks (decided 2026-11-05: self-regression)
-
vitest bench(tinybench) over the vendored generic + React fixtures, comparing:- the working tree,
- the last published version (aliased devDep, e.g.
react-fast-compare-published@npm:[email protected], bumped after each release), and [email protected]as the upstream baseline we forked from.
-
pnpm benchlocally, and a report-only CI job writing the table to the job summary. Not gated; CI runners are too noisy for that. - Remove
benchmark/index.js,benchmark(the npm package),assets/benchmarking.png, the README chart and the Google Sheet step in CONTRIBUTING. The README keeps a short "Benchmarking" section on how to run it. - Why keep it at all: speed is the library's whole pitch, and the next source changes (#139 Temporal, #50 Sets) touch the hot path.
Phase 5: Docs and housekeeping
- README: npm-only install; fix "tracks
[email protected]" (it's 3.1.3); fix the broken LICENSE link (blob/readme/LICENSE); update badges; settle maintenance status/branding (Formidable → Nearform?). The "ES5 / IE9" claims stay true in 3.x and change in 4.0. - CONTRIBUTING rewritten for pnpm, Vitest, oxlint/oxfmt, JSDoc types, changesets, canary,
dep:check, bench. -
.github/pull_request_template.md: drop the bundle-badge andtest-ts-*items. - Close or settle: Dependabot PRs #122/#124/#128 (Karma tree, gone), #12 (Sauce Labs → Playwright), #24 (Backstroke, obsolete), #70 (alerts).
- Delete stale remote branches:
ci-provenance,experiment/es6-circ-refs,feature/es6-extras,fix/missing-patch,task/update-dependencies,viper1104-dom, the dependabot ones, andchangeset-release/masterwhen idle. - Optional: move
react-fast-compare-Hero.png(~510 KB, not published) into.github/.
After the uplift (source, PRs straight to master)
- #139 Temporal → 3.x release from the new pipeline. Check
toString-based Temporal types beforevalueOf(or detectTemporal.*and use.equals). Bench before/after. - #137
sideEffects: false: rides along with #139 (manifest-only, safe for 3.x). - PR #135 (NaN comment): rebase after the oxfmt churn and merge.
- Later and undecided: #50 (Set by value; perf cost), #68 (debug mode), #114 / PR #115 (function comparison, declined so far as unsafe by default), plain-Preact vnodes (see probe).
4.0.0 (later; sketch only)
- ESM + CJS with an
exportsmap,sideEffects: false, modern target (drop ES5/IE;for...ofinstead of the hand-lowered iterators),.d.tsgenerated from JSDoc (.d.ctswithexport =,.d.mtswithexport default). - Open question for then: author ESM and generate the CJS (tsdown/rolldown, keeping
module.exports = isEqual), or keep CJS source with a thin ESM wrapper (no dual-package hazard, since the function is stateless). - Changeset migration note: deep imports (
react-fast-compare/index.js) stop resolving underexports.
Decision log
| Date | Decision | Why |
|---|---|---|
| 2026-11-05 | Integration branch chore/2026-uplift, PRs into it, merge commit to master |
Same as victory; keeps master releasable. |
| 2026-11-05 | Keep master as the default branch |
No churn for contributors, links and dependents' tooling. |
| 2026-11-05 | Uplift has no published change; #139 ships as 3.x from the new pipeline; ESM/exports/modern target in a later 4.0.0 |
Most of the ~107M monthly downloads come through ^3/^2 ranges. A 3.x fix reaches them; a 4.0 wouldn't until dependents bump. Keeps the pipeline's first release low-risk. |
| 2026-11-05 | Types via JSDoc + checkJs on plain JS (llm-splitter pattern), not a TS port |
No build step for the code. TS 7 emits today's index.d.ts exactly from a 5-line JSDoc block. |
| 2026-11-05 | Benchmarks: self-regression (tree vs last published vs fast-deep-equal), report-only | Speed is the pitch, and upcoming fixes touch the hot path. Other libraries' numbers don't help maintain this one. |
| 2026-11-05 | No task runner; plain scripts | Single package, sub-second tasks. |
| 2026-11-05 | Tokens disallowed on npm right after the canary, not after #139 | The canary proved OIDC end to end; no reason to keep a token path open. |
| 2026-11-05 | Canary 3.2.3-canary-* stays on next |
Harmless; the next canary replaces it. |
| 2026-11-05 | Scripts: check, format, test as the entry points, plus only the sub-scripts they call |
Same shape as victory; fewer names to learn and keep working. |
| 2026-11-05 | Vitest (Node + browser mode/Playwright) over node:test + another browser runner |
One spec set, one tool, both environments; matches victory. |
| 2026-11-05 | Vendor fast-deep-equal's spec cases, drop the git dependency | Upstream frozen since 2020; git deps are slow and fragile. |
| 2026-11-05 | npm trusted publishing and the GitHub environment are set up by hand, not scripted | One package; victory's npm-trust.sh only pays off across 29. |
| 2026-11-05 | Dev dependencies kept to the strict minimum; builder removed |
Every dependency is upkeep (alerts, dep:check, release-age holds). A single-file library needs few tools. |
| 2026-11-05 | Codecov removed early (#141), not replaced until Vitest coverage | The codecov@3 uploader crashes in CI; nyc still prints a summary. |
| 2026-11-05 | Dev files follow victory: .nvmrc (26), .editorconfig, tracked .vscode/ recommendations, oxc configs, .ncurc.cjs, zizmor/pinact configs |
Same habits across our repos. |
- 主要语言
- JavaScript
- 星标
- 1.7k
- 派生
- 54
- 平均合并
- 1 小时 11 分钟
- 30 天内合并 PR
- 2
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
FormidableLabs/react-fast-compare 的其他 Issue
-
bug
难度 2/5 1-3 小时 新手友好度 82/100
-
难度 1/5 1 小时以内 新手友好度 45/100
-
Add ability to compare functions可能已有人在做 @quantizor 于 1480 天前认领。 未关闭
难度 5/5 一周以上 新手友好度 25/100
FormidableLabs/react-fast-compare#114 · 9 条评论 ·
-
investigate
难度 4/5 3-5 天 新手友好度 25/100
FormidableLabs/react-fast-compare#70 · 2 条评论 ·
-
enhancement
难度 5/5 一周以上 新手友好度 30/100
FormidableLabs/react-fast-compare#68 · 11 个 reaction ·
查看 FormidableLabs/react-fast-compare 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 78/100
guybedford/es-module-lexer#266 ·
-
bug documentation
难度 1/5 1-3 小时 新手友好度 88/100
apify/apify-docs#3206 ·
维护者通常 1 天内回复
-
enhancement good first issue
难度 2/5 1-3 小时 新手友好度 90/100
anhnt256/cursor-swarm#21 ·
-
难度 2/5 1-3 小时 新手友好度 72/100
-
documentation good first issue
难度 2/5 1-3 小时 新手友好度 74/100
zerohourzulu/irontrellis#16 ·