Skip to content

EPBDS-16211: make the orphan scan read TypeScript again - #152

Merged
AlexSamBY merged 1 commit into
masterfrom
docs-plan-structure
Oct 2, 2026
Merged

AlexSamBY merged 1 commit into
masterfrom
docs-plan-structure

Conversation

@AlexSamBY

Copy link
Copy Markdown
Member

What changes

These are the two findings that #151 recorded and left for a change of their own. Both are in docs/UPGRADE-PLAN.md.

The orphan scan in Appendix B reads TypeScript again

It had checked no component since the TypeScript migration. It looped over src/core/components/*.js, and the pack has no .js file now: it has 42 .ts/.tsx files.

  • In zsh, the glob fails: no matches found: src/core/components/*.js, and nothing is printed.
  • In bash, the loop runs once, on the literal pattern. It prints one line, 198 *: the pattern becomes /*', which matches every file that contains a '.

The new command finds the pack's .ts and .tsx files and strips either extension. It matches an import in either quote style, and leaves out the component's own file under either extension.

How I checked it:

  • Run as the plan prints it, in bash and in zsh, it scans all 42 files, with the same output in both shells.
  • It reports nothing as an orphan today: no pack file reads 0.
  • It can still fail: a file I planted in the pack with no importer read 0. I then deleted it.

Its notes on reading the output are re-measured. The old ones described what the scan showed on 2026-09-17:

  • index used to be "the only remaining 0". It now reads 5, and none of the five is the pack's barrel: three import the engine's index, and two are doc comments. The barrel itself is imported by directory path (from '../components'), which the pattern cannot see.
  • The same-name trap still applies, with a current example. files reads 5, and two of the five import modules/variables/files.ts. The old example, Tabs, went with the H6 rename.
  • A count includes the demo. StandaloneTabs reads 1, and that one importer is the demo's NavTabs.

Appendix A's structure facts

Each fact that is gone is struck through and followed by the change that removed it, as the plan marks its other closed items:

Two more fixes in the same bullet:

There is no code change.

Gates

All exited 0 on this branch, test:coverage on its second run:

  • lint:js, lint:css, typecheck, typecheck:contract;
  • docs:props:check, docs:views:check, css:fixture:check;
  • test:env-flags, build;
  • test:coverage, thresholds included;
  • test:pack, test:pack:peers, test:types;
  • test:react16, test:react17, test:react19, 2854 tests each, with no worker crash;
  • test:e2e, 41 tests;
  • the manifest contract.

The first test:coverage run failed one test, and this PR is not the cause. examples.strict-mode.test.js compares each example's DOM after an edit, with and without StrictMode. On tableForm the two runs read different DOM:

  • the plain run read the date picker still open, with the typed text;
  • the strict run read it closed, with the value reverted.

This is the same kind of race #149 fixed, through animation frames instead of timers:

  • The picker opens, aligns and closes on animation frames, through rc-util/raf. EPBDS-16211: compare the StrictMode runs on virtual time #149 left requestAnimationFrame on real time.
  • The 400 ms the test waits after an edit are virtual, and they take 1–3 ms of real time. Whether a real frame lands inside them is chance. With the test's own timer setup and no coverage, 2 of 3 plain runs had no frame there, and read the picker open.
  • With frames suppressed, both modes read the picker open. So the comparison depends on whether a frame ran.

The rerun passed all 2854 tests. The fix belongs in the test, in a change of its own.

🤖 Generated with Claude Code

Appendix B's orphan scan looped over src/core/components/*.js. Since
the TypeScript migration the pack has no .js file, so the scan checked
no component. zsh rejects the empty glob. bash runs the loop once, on
the literal pattern, and prints a single "198 *" line.

The scan now finds the pack's .ts and .tsx files and gives the same
output in bash and zsh. Re-run today, no pack file reads 0. A planted
file with no importer reads 0. Its notes on reading the output are
re-measured: the barrel's count is an artefact, files shares its name
with modules/variables/files.ts, and the Tabs example went with the H6
rename.

Appendix A's structure facts list what is gone as gone, each with the
change that removed it:
- the engine/form-modules cycle (#69);
- Text's import of modules (#64);
- the demo entries at the src/ root (#63).
They also give the scan's new result, and the engine TabList's current
path.
@AlexSamBY
AlexSamBY merged commit 0f83ef2 into master Oct 2, 2026
5 checks passed
@AlexSamBY
AlexSamBY deleted the docs-plan-structure branch October 2, 2026 09:56
AlexSamBY added a commit that referenced this pull request Oct 2, 2026
…me (#153)

## What changes

**`examples.strict-mode.test.js` now runs the animation frames on
virtual time, as well as the timers.** The comparison still raced
against the wall clock through the frames, which #149 left real.

### The failure

A local `npm run test:coverage` run failed once, while #152 was gated.
The test was "every example under StrictMode › tableForm renders, and
reports, what it does without it", at
`expect(strict.edited).toBe(plain.edited)`, and the rerun passed. After
the edit, the two runs read the first date input differently:
- **the plain run** read it with `value="an edit"`, and its picker still
open, not yet aligned (`left: -1000vw`);
- **the strict run** read it closed
(`ui-render-picker-dropdown-hidden`), with the value reverted to
`01-01-2022`.

### Why

- **The picker opens, aligns and closes on animation frames,** through
`rc-util/raf`. You can see it in rc-picker 4.11.3's `useDelayState`,
`useLockEffect` and `Selector/Input`.
- **#149 faked the timers and left `requestAnimationFrame` real.** Its
comment said that nothing in the corpus needs frames faked. That was
wrong for the picker.
- **The 400 ms the test waits after an edit are virtual, and they pass
in 1–3 ms of real time.** So whether a real frame landed in them was
chance. A probe that counted frames found 2 of 3 plain runs on React 18
with no frame in that window, and each of them read the picker open.

### The fix

`exercise()` no longer lists `requestAnimationFrame` and
`cancelAnimationFrame` in `doNotFake`. A faked frame runs every 16 ms of
the run's own clock.

`Date`, the microtask queues and `setImmediate` stay real, as before.
React's act and its scheduler use them, and no React version uses frames
to schedule.

### Proof

All three checks ran on React 16.14, 17.0.2, 18.3.1 and 19.3.0.

**The new reads are the settled reads.** For each of the 76 runs (38
examples × 2 modes), I compared the mounted DOM, the edited DOM and the
console output with the old reads taken after a 40 ms real hold, when
the frames have run. All are identical.
- A 40 ms hold no longer changes any read.
- The new runs use no real frame.
- With the old setup, only `tableForm` changed between three identical
runs. That happened on React 16 and 18. On React 19 all three old runs
read the picker open, against the settled read. React 17's sample
happened not to vary.

**The race is reproduced and closed.** I added a probe that withheld
real frames from the plain run, and held the strict run for 40 ms after
the blur, as a slow machine does.
- The old test failed on `tableForm` on React 16 and 18, with the same
`expect(strict.edited).toBe(plain.edited)` failure.
- The new test passed on both.
- The probe is removed.

**It still catches StrictMode bugs.** I put back the dropdown mount flag
that this test found when it was written. The new test failed on the
same 7 examples as before.

**It is not slower.** The file takes 1.4–1.7 s a leg, as it did.

**It holds under the conditions that failed.** I ran the whole suite
five times with `jest --coverage --runInBand`, the shape of
`test:coverage`. All five passed, 2854 tests each.

`docs/UPGRADE-PLAN.md` records the change beside the test's virtual-time
note (§9.3 step 7).

## Gates

All exited 0 on the first run, on this branch:
- `lint:js`, `lint:css`, `typecheck`, `typecheck:contract`;
- `docs:props:check`, `docs:views:check`, `css:fixture:check`;
- `test:env-flags`, `build`;
- `test:coverage`, thresholds included;
- `test:pack`, `test:pack:peers`, `test:types`;
- `test:react16`, `test:react17`, `test:react19`, 2854 tests each, with
no worker crash;
- `test:e2e`, 41 tests;
- the manifest contract.

The number of tests is unchanged.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.

1 participant