react-native-doctor-ci 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +196 -0
- package/dist/cli.cjs +1677 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1654 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +1546 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +736 -0
- package/dist/index.d.ts +736 -0
- package/dist/index.js +1480 -0
- package/dist/index.js.map +1 -0
- package/package.json +76 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Amrith Vengalath
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# react-native-doctor-ci
|
|
2
|
+
|
|
3
|
+
Fail pull requests that add abandoned, non-New-Architecture, or npm-deprecated React Native dependencies — with inline annotations and a policy-as-code allowlist.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/react-native-doctor-ci)
|
|
8
|
+
[](https://github.com/AmrithVengalath/react-native-doctor-ci/actions/workflows/ci.yml)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
## Why
|
|
13
|
+
|
|
14
|
+
Unmaintained dependencies are a perennial top-5 pain in React Native surveys, and the cost keeps climbing now that the New Architecture is the default: a package that looked fine when it was added quietly stops getting releases, never gains New Architecture support, or gets deprecated on npm — and you find out during an upgrade, months after the PR that introduced it merged.
|
|
15
|
+
|
|
16
|
+
Lookup tools exist (the React Native Directory tells you a package's status if you go ask). What's been missing is **enforcement**: the check that runs on every PR, fails when someone adds a dying dependency, and points at the exact line — so the conversation happens at review time, when swapping the package costs five minutes instead of a quarter.
|
|
17
|
+
|
|
18
|
+
`rn-doctor` is that check. Pure TypeScript, zero native code, one command.
|
|
19
|
+
|
|
20
|
+
## Quickstart
|
|
21
|
+
|
|
22
|
+
### GitHub Action (recommended)
|
|
23
|
+
|
|
24
|
+
Create `.github/workflows/rn-doctor.yml`:
|
|
25
|
+
|
|
26
|
+
```yaml
|
|
27
|
+
name: Dependency health
|
|
28
|
+
|
|
29
|
+
on:
|
|
30
|
+
pull_request:
|
|
31
|
+
|
|
32
|
+
permissions:
|
|
33
|
+
contents: read
|
|
34
|
+
|
|
35
|
+
jobs:
|
|
36
|
+
rn-doctor:
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v7
|
|
40
|
+
with:
|
|
41
|
+
fetch-depth: 0 # required for changed-only (merge-base diff)
|
|
42
|
+
|
|
43
|
+
- uses: AmrithVengalath/react-native-doctor-ci/action@v0.1.0
|
|
44
|
+
with:
|
|
45
|
+
changed-only: "true"
|
|
46
|
+
base: origin/${{ github.base_ref }}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
That's the whole setup. PRs that add or upgrade a dependency failing your policy get a red check with an inline annotation on the offending `package.json` line. A ready-to-copy workflow and a fully commented policy file live in [`example/`](example/).
|
|
50
|
+
|
|
51
|
+
### CLI
|
|
52
|
+
|
|
53
|
+
Run it directly in any project with a `package.json`:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
npx --yes --package react-native-doctor-ci rn-doctor
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
(The package name and the binary name differ, hence `--package`. Once installed as a devDependency, it's just `rn-doctor`.)
|
|
60
|
+
|
|
61
|
+
## GitHub Action reference
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
- uses: AmrithVengalath/react-native-doctor-ci/action@v0.1.0
|
|
65
|
+
with:
|
|
66
|
+
version: "0.1.0" # npm version/dist-tag to run (default: latest)
|
|
67
|
+
policy: ".rn-doctor.yml" # policy file path (default: auto-detect)
|
|
68
|
+
changed-only: "true" # only deps added/changed vs base (default: "false")
|
|
69
|
+
base: origin/main # base ref for changed-only (default: origin/main)
|
|
70
|
+
workspaces: "false" # also check workspace package.jsons
|
|
71
|
+
annotations: "true" # inline GitHub annotations (default: "true")
|
|
72
|
+
token: ${{ github.token }} # for GitHub repo enrichment (default: workflow token)
|
|
73
|
+
working-directory: "." # where package.json lives
|
|
74
|
+
node-version: "22" # Node to set up (rn-doctor needs >= 20)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Notes:
|
|
78
|
+
|
|
79
|
+
- `changed-only` requires `actions/checkout` with `fetch-depth: 0` — the diff is computed against the merge-base of the PR branch and `base`, same as GitHub's three-dot compare. Without history the run fails with an actionable message telling you exactly that.
|
|
80
|
+
- The default `token` (the workflow's own `GITHUB_TOKEN`) is enough; it's only used to read public repo metadata (archived state, last push). Without any token those fields degrade to `unknown` with a warning — the run never fails because of rate limits.
|
|
81
|
+
- Pin `version` for reproducible CI; `latest` is convenient but floats.
|
|
82
|
+
|
|
83
|
+
## CLI reference
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
rn-doctor [options]
|
|
87
|
+
|
|
88
|
+
--json Machine-readable JSON report (stable-ordered)
|
|
89
|
+
--sarif SARIF 2.1.0 report (for code-scanning upload)
|
|
90
|
+
--policy <path> Policy file path (default: .rn-doctor.yml if present)
|
|
91
|
+
--changed-only Only check deps added or changed vs the base ref
|
|
92
|
+
--base <ref> Base ref for --changed-only (default: origin/main)
|
|
93
|
+
--workspaces Also check every workspace package.json
|
|
94
|
+
--no-cache Bypass the enrichment cache (read and write)
|
|
95
|
+
--annotations Force GitHub annotations on (auto in GitHub Actions)
|
|
96
|
+
--no-annotations Force GitHub annotations off
|
|
97
|
+
-v, --version Print the version
|
|
98
|
+
-h, --help Show help
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Exit codes** (stable contract — CI depends on it):
|
|
102
|
+
|
|
103
|
+
| Code | Meaning |
|
|
104
|
+
| ---- | ------- |
|
|
105
|
+
| `0` | Clean. Warnings, notes, and allowlisted findings are OK. |
|
|
106
|
+
| `1` | Policy errors found. |
|
|
107
|
+
| `2` | Tool failure: bad flags, unreadable `package.json`, invalid policy file, git failure under `--changed-only`. |
|
|
108
|
+
|
|
109
|
+
**Environment**: `GITHUB_TOKEN` (optional, enables GitHub repo enrichment), `NO_COLOR` (disables ANSI color), `GITHUB_ACTIONS=true` (auto-enables annotations).
|
|
110
|
+
|
|
111
|
+
Only `dependencies` are checked — `devDependencies` never ship in your app.
|
|
112
|
+
|
|
113
|
+
## Policy: `.rn-doctor.yml`
|
|
114
|
+
|
|
115
|
+
Everything is optional; omitted keys use the defaults shown. Unknown keys are rejected loudly, so a typo can't silently weaken the policy.
|
|
116
|
+
|
|
117
|
+
```yaml
|
|
118
|
+
rules:
|
|
119
|
+
newArchitecture: error # directory says "not New Arch supported"
|
|
120
|
+
newArchUnknown: warn # New Arch support unknown (missing data -> warn, not error)
|
|
121
|
+
lastPublish: # staleness of the latest npm publish
|
|
122
|
+
warnMonths: 12
|
|
123
|
+
errorMonths: 24 # or the string "off"
|
|
124
|
+
githubArchived: error # GitHub repository is archived
|
|
125
|
+
npmDeprecated: error # latest version deprecated on npm
|
|
126
|
+
directoryUnmaintained: warn # RN Directory "unmaintained" flag
|
|
127
|
+
|
|
128
|
+
scope: rn-native-only # or all-deps
|
|
129
|
+
|
|
130
|
+
allow: [] # see "Unblocking a PR" below
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Each rule takes `error | warn | off` (except `lastPublish`, which takes thresholds or `"off"`).
|
|
134
|
+
|
|
135
|
+
**How the data is gathered.** Each dependency is enriched in parallel from the npm registry (publish time, deprecation, `codegenConfig`), the [React Native Directory](https://reactnative.directory) (New Architecture support, unmaintained flag, GitHub URL), and the GitHub API (archived, last push — token optional). Results are cached in `.rn-doctor-cache.json` for 24 hours (add it to `.gitignore`; `--no-cache` bypasses).
|
|
136
|
+
|
|
137
|
+
**New Architecture verdicts** are tiered honestly: directory says supported → pass; directory says unsupported → `newArchitecture` fires; unknown but the package ships `codegenConfig` → pass with an informational note; unknown otherwise → `newArchUnknown` fires (a warning by default — missing data is not the same as a dead package).
|
|
138
|
+
|
|
139
|
+
**Scope.** `rn-native-only` (default) checks packages that actually couple to React Native: listed in the RN Directory, peer-depend on `react-native`, or ship `android`/`ios` directories. `all-deps` checks everything under `dependencies`.
|
|
140
|
+
|
|
141
|
+
## Unblocking a PR (allowlist)
|
|
142
|
+
|
|
143
|
+
Sometimes you ship with a flagged package on purpose. One YAML entry unblocks the PR while keeping the finding visible in every report:
|
|
144
|
+
|
|
145
|
+
```yaml
|
|
146
|
+
allow:
|
|
147
|
+
- package: react-native-legacy-thing
|
|
148
|
+
reason: "Replacement planned for Q4; tracked in TICKET-123"
|
|
149
|
+
expires: 2027-01-31
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
- Suppressed findings still appear in output (marked as allowed) — nothing disappears silently.
|
|
153
|
+
- `expires` is optional but recommended: past that date the allow stops suppressing and findings **escalate to errors**, so exceptions can't quietly outlive their justification.
|
|
154
|
+
- Every finding's message suggests the exact allowlist entry to add, so unblocking is copy-paste.
|
|
155
|
+
|
|
156
|
+
## Output formats
|
|
157
|
+
|
|
158
|
+
- **Pretty** (default): one block per finding — severity badge, message, allow-reason if suppressed, and an evidence link (npm/directory/GitHub) so you can verify every claim.
|
|
159
|
+
- **`--json`**: stable-ordered, timestamp-free (`version: 1`) — safe to snapshot or post-process.
|
|
160
|
+
- **`--sarif`**: SARIF 2.1.0, validates against the official schema. Upload to GitHub code scanning:
|
|
161
|
+
|
|
162
|
+
```yaml
|
|
163
|
+
- run: npx --yes --package react-native-doctor-ci rn-doctor --sarif > rn-doctor.sarif
|
|
164
|
+
continue-on-error: true # let the SARIF upload happen; the gate is separate
|
|
165
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
166
|
+
with:
|
|
167
|
+
sarif_file: rn-doctor.sarif
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
- **GitHub annotations**: emitted automatically inside GitHub Actions, resolved to the dependency's real line in `package.json` (string-escape-aware — a same-named key under `scripts` can't false-match).
|
|
171
|
+
|
|
172
|
+
## Changed-only and monorepos
|
|
173
|
+
|
|
174
|
+
`--changed-only` is the flagship PR mode: it diffs `dependencies` against the merge-base with `--base` and checks only additions and spec changes (downgrades and protocol changes count too — they're re-checked). Fast, and zero noise from pre-existing debt: adopting rn-doctor on a 5-year-old app doesn't mean fixing 30 findings before the check goes green.
|
|
175
|
+
|
|
176
|
+
`--workspaces` walks npm/yarn `workspaces` globs or `pnpm-workspace.yaml` and checks every workspace manifest, with findings grouped per manifest and annotations pointing at the right file. Composes with `--changed-only`.
|
|
177
|
+
|
|
178
|
+
## FAQ
|
|
179
|
+
|
|
180
|
+
**Does it check devDependencies?** No — they don't ship in your app. Scope decisions live in the policy file, not flags.
|
|
181
|
+
|
|
182
|
+
**Is it a security scanner?** No. CVE auditing is `npm audit` / OSV territory. rn-doctor answers a different question: *is this dependency alive and does it have a future on the New Architecture?*
|
|
183
|
+
|
|
184
|
+
**Does it fix anything?** No — it tells you what's wrong and what to do (allowlist entry, `npx expo install --fix`, Renovate), then gets out of the way.
|
|
185
|
+
|
|
186
|
+
**What if the RN Directory is wrong about a package?** Every finding carries an evidence link so you can check, and a false positive costs one allowlist line. File a correction with the directory — everyone benefits.
|
|
187
|
+
|
|
188
|
+
**Why is `yaml` the only runtime dependency?** The policy file and `pnpm-workspace.yaml` are YAML, and a spec-compliant YAML parser is not a weekend project — this one is worth its weight. Everything else (HTTP, caching, git diffing, glob matching, SARIF, annotations) is hand-rolled on Node built-ins; less supply chain in a tool whose whole job is judging dependencies.
|
|
189
|
+
|
|
190
|
+
## Contributing
|
|
191
|
+
|
|
192
|
+
Issues and PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). The project follows a phased roadmap; out-of-scope items (security auditing, auto-fixing, a hosted service) are listed there.
|
|
193
|
+
|
|
194
|
+
## License
|
|
195
|
+
|
|
196
|
+
[MIT](LICENSE) © Amrith Vengalath
|