feat(codemod): add react-simplikit-codemod with the mobile-to-root transform - #458
Draft
mnxmnz wants to merge 26 commits into
Draft
feat(codemod): add react-simplikit-codemod with the mobile-to-root transform#458mnxmnz wants to merge 26 commits into
mnxmnz wants to merge 26 commits into
Conversation
…simplikit in package.json
🦋 Changeset detectedLatest commit: e0c48c4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Contributor
|
Size Change: 0 B Total Size: 104 kB ℹ️ View Unchanged
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #458 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 58 67 +9
Lines 1664 2184 +520
Branches 500 706 +206
==========================================
+ Hits 1664 2184 +520 🚀 New features to boost your workflow:
|
… and one-field manual type
- compare imported names, not local ones, so an alias collision falls back to a plain rewrite - add react-simplikit per dependency field instead of once per manifest - apply ignore globs, extension and package.json rules to explicitly named paths - keep imports inside declare module blocks and imports carrying comments in place - report manual follow-ups even when no file changed - rewrite import-equals require, require.resolve and the vitest import helpers
… packed bin - accumulate per-file failures into the run result instead of throwing, so the report survives and one bad manifest no longer sinks a dry run - exit 1 with every failure named under "Could not be processed" - add an e2e case that packs the tarball and runs the bin it declares
Codemod practice is to bail with a reason rather than leave a refusal invisible (LibCST SkipFile, styled-components-to-stylex warnings). A silently unmerged import invites the user to merge it by hand, which reintroduces exactly the binding change the codemod avoided. Reports the two refusals a reader could plausibly undo — a colliding local name and an attached comment — with the line. Adds an idempotency test.
… on a wrapper formatJson wrapped one JSON.stringify call in a module and a 62-line test. Three of those four tests asserted JSON.stringify's own behaviour; the fourth pinned the field set one layer above where a consumer reads it, so it could not see the CLI picking the wrong formatter or anything else writing to stdout. The key set is now asserted on the real process output. formatHuman keeps its unit tests — it has five branches worth localizing.
- tie the merge guard to the range it actually deletes, and detect comments with the TypeScript scanner instead of a substring scan. aa72201 widened the range to the whole statement and dropped the doc comment stating the precondition that made the scan exact, so a banner above the first import blocked merges while a trailing comment on the import's own line was orphaned - glob with dot: true so .storybook and friends are scanned; ignore .pnp.* - anchor user ignore globs to cwd, so --ignore 'legacy/**' stops being a no-op - raise an existing react-simplikit range that sits below the floor, keep non-registry protocols instead of replacing them with a caret range, and widen peer ranges with >= rather than narrowing them with ^ - report a file that names the package where no import could be rewritten - only claim a clean scan when nothing came back at all - read --debug from the parsed option and carry it into per-file failures The formatHuman guard added in ed23039 asserted a string the function no longer emits, so it could not fail; three manifest tests and two merge tests encoded the old behaviour and were corrected against the reproductions.
- list the refusals the report names and the ones it does not, instead of claiming the first two of three are reported when it reports the first and third - add `failed` to the documented --json object and state that all six keys are always present - drop `npm install react-simplikit` from the post-run step: it overwrites the range the codemod just wrote, so a bare reinstall is what is wanted - record the dot-directory scan, the cwd-relative --ignore, the raised version floor, the preserved non-registry protocols and the unclassifiable-mention note - CLAUDE.md: add packages/codemod to the structure tree, and name all three coverage exclusions with the reason each one holds - llms.txt: point agents at the codemod instead of stating a stale floor - verifyDocsI18n: say what the assertion guards now that it guards six commands Also corrects the refusal wording — merging strands a comment on its own line rather than dropping it, which is what the reproduction showed.
…r producers - FileResult becomes a discriminated union; the reporter branches on `kind` instead of the proxy predicate `dependencies.length > 0` - dissolve the top-level `types.ts` bucket: each type moves next to its sole producer, and only the five shared across the transform's siblings stay in `mobileToRoot/types.ts` - flatten `src/transforms/mobileToRoot/` to `src/mobileToRoot/` - restore `formatJson` beside `formatHuman` so both renderers are siblings; the e2e keeps pinning the stdout contract - the e2e resolves the CLI from the manifest `bin` instead of hard-coding `dist/cli.mjs`, so a `bin` left behind by a build-output rename now fails
…ed files The reporter refactor changed the published output without the docs following: - SKILL.md showed `changed[]` entries carrying both `changes` and `dependencies` with one always empty. Entries now carry `kind` and only the matching key, so the example and the key list are rewritten from real output - SKILL.md claimed `dependencies[].added` is omitted when the range already satisfied the floor. It is `null` - CLAUDE.md named `src/types.ts` among the coverage exclusions and `transforms/` in the package tree; neither exists. The dead `src/types.ts` entry is dropped from the vitest exclude list too - name the merge condition instead of "where it can", and call the target the root entry throughout Also: report a target outside the current directory by its absolute path rather than a chain of `..` segments, and say "1 file" instead of "1 file(s)".
…f the npm tarball SKILL.md sat in packages/codemod but `files` is `dist/**/*`, so it never reached the published tarball — and an npm tarball is not where any agent looks for a skill. It moves to packages/plugin/skills/react-simplikit-codemod, next to the generated catalog skill, and ships through the marketplace the plugin already publishes. The two skills are maintained differently, so `verifySkill` now separates the checks: the frontmatter and length limits apply to both, while the regeneration diff stays on the generated one. It also pins the version floor — `MIN_RUNTIME_VERSION` is repeated in prose in two files, and it was already wrong once at 0.1.1.
…y component `isBelowFloor` was `FLOOR.some((part, index) => version[index] < part)`, which asks whether ANY component is below its own floor component rather than letting the first differing component decide. Against a 0.2.0 floor that ranks 1.0.0, 1.1.0, 2.0.0 and 10.0.0 as below it, so the codemod overwrote a consumer's `react-simplikit` range with `^0.2.0` — a downgrade across a major, reported in the same words as the documented floor raise. `^1.5.0` survived only because 5 is not below 2. The same line treated "contains three dot-separated digits" as "is a registry range", so `workspace:^0.1.1` and `file:../pkg-0.1.1.tgz` were flattened to a registry range while their digit-free forms were kept. Protocol specs are now detected by their scheme, kept on both sides of the swap, and reported — which is what README.md already promised. Latent today: react-simplikit is at 0.1.0, and for an all-0.x world the old predicate collapses to a correct `minor < 2`. It goes live the day a major ships while consumers are still running the published codemod. The existing tests picked `^0.1.0` and `^0.9.0` — same major, so 100% coverage and the bug coexisted. The new cases cover a higher major with a lower minor, both protocol forms, the floor boundary itself, and a wildcard range.
`--ignore ./legacy/**` filtered nothing. Scoping joined the pattern onto the cwd unchanged, and the resulting `/./` never matches in micromatch, so the run reported no error and skipped no file. The leading `./` is now stripped before the join. `--debug` replaced the failure reason with a raw stack rather than adding to it, so asking for more detail lost the actionable line — "package.json is not valid JSON" became a bare SyntaxError. The stack now follows the reason. The test that covered `--debug` asserted the reason starts with `Error: `, which pinned the old behaviour rather than the intent; it now asserts the plain reason survives inside the debug one.
…s on `codemod-node-floor` ran on `node: ['22']`, which resolves to the newest 22.x and never exercises the `>=22.17.0` in engines — the floor the job is named for. It now pins 22.17.0. The MIN_RUNTIME_VERSION guard added with the skill move matched a bare `0.2.0`, which the illustrative JSON sample in SKILL.md also contains, so the prose around it could go stale with the assertion still green. It now matches the prose form `react-simplikit@0.2.0`. Also: the manual-note sentence said the codemod could not be rewritten when it meant the reference could, and SKILL.md documented one of the two conditions that produce `added: null`.
…ngines floor Pinning the floor job to 22.17.0 turned it red: tsdown fails to import `unrun` on that version. But engines.node constrains the runtime a consumer runs the published bin on, not the toolchain that builds it — a consumer never runs tsdown. Building on the floor was never what the job was for. The build now runs on the repo's .nvmrc node and only the CLI is exercised on the floor, which is what the job's name has claimed all along. `test:e2e:run` is `test:e2e` without the build step, for the second half.
The pre-existing `spec` matrix already runs `test:coverage` across every workspace on node 22 and 24, so the codemod's unit tests have been covered on node 22 since the workspace was added. The bespoke job only added the e2e on 22.17.0 rather than 22.latest — and that floor is a number this branch itself picked, so the job largely existed to defend its own constant. Removing it also stops the e2e running twice per PR. `quality (codemod)` keeps it, and that is the only job that exercises the built bin.
Removes the `ponytail:` marker, which named a review tool rather than anything about this repo, and drops the comments that restate their own code. What stays is the handful a reader cannot recover: why the floor must name a published version, why protocols are detected by scheme instead of digits, why the floor comparison is not per-component, why a leading `./` is stripped, why the skill gate matches the prose form, and why the e2e reads `bin` from the manifest. Each guards a simplification that would reintroduce a bug this branch fixed.
`engines.node: ">=22.17.0"` was picked when this package was scaffolded and nothing verified it: the spec matrix runs node 22 and 24, never that patch. react-simplikit declares no engines at all, so the CLI now makes no claim either rather than an untested one. npm treats the field as advisory by default, so removing it changes no install that was working. The skill's Node precondition goes with it, for the same reason.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Closes #454. Adds
react-simplikit-codemod, a bin-only CLI that migrates consumers off@react-simplikit/mobile— absorbed into thereact-simplikitroot entry in #437 — onto the root entry.What each reference to the old package becomes:
flowchart TD R["@react-simplikit/mobile reference"] --> W{found where} W -->|dependency field| M["swap to react-simplikit@^0.2.0"] W -->|resolutions or overrides| N["report only, meaning differs per package manager"] W -->|source import| E{file already imports react-simplikit} E -->|no| P["rewrite the specifier in place"] E -->|yes| S{"merging would rebind a name, or strand a comment"} S -->|no| F["fold the bindings into that import"] S -->|yes| C["rewrite in place, then report"]Source files are parsed with the TypeScript compiler and edited by offset, so formatting, comments and quote style survive untouched;
package.jsonis the exception and gets reserialized. The run never prompts and is idempotent — a second run finds nothing to change.--dry-runpreviews,--jsonemits a machine-readable report, and exit1means some file could not be read, parsed or written while every other file still migrated.Full surface, flags and exit codes are in
packages/codemod/README.md; the agent-facing JSON contract ships as a skill inpackages/plugin/skills/react-simplikit-codemod/.Verification
quality (codemod)added tointegration.yml; thespecmatrix already covers node 22 and 24Checklist
yarn run fixto format and lint the code and docs?yarn run test:coverageto make sure there is no uncovered line?