{"_id":"@aadi49/preflight","name":"@aadi49/preflight","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@aadi49/preflight","version":"0.1.0","description":"See what will actually break before you upgrade an npm dependency.","type":"module","bin":{"preflight":"bin/cli.js"},"main":"./src/cli.js","engines":{"node":">=18.3.0"},"scripts":{"preflight":"node ./bin/cli.js","build:action":"node ./scripts/build-action.mjs","test":"node --test \"test/**/*.test.js\""},"keywords":["npm","upgrade","dependencies","breaking-changes","static-analysis"],"homepage":"https://preflight-umber.vercel.app","repository":{"type":"git","url":"git+https://github.com/AadiSharma49/preflight.git"},"bugs":{"url":"https://github.com/AadiSharma49/preflight/issues"},"license":"MIT","dependencies":{"@babel/parser":"^7.29.8","@babel/traverse":"^7.29.8","ignore":"^7.0.6","semver":"^7.8.5"},"devDependencies":{"esbuild":"^0.28.1"},"_id":"@aadi49/preflight@0.1.0","gitHead":"43d7b0ca60cfb96c6b2b7a7586e91958ef801cde","_nodeVersion":"20.20.0","_npmVersion":"10.8.2","dist":{"integrity":"sha512-lKU3HLt00OeE2LeoCUhAGJVtwVPqADr7aL2bS23rQwxirVJOr36JsqxoOKyz3xfM0DsrV4TQ2rD3tXxIcOUn9g==","shasum":"45c8383a94c3fc2053383fb972bdc923d9fc8ff2","tarball":"https://registry.npmjs.org/@aadi49/preflight/-/preflight-0.1.0.tgz","fileCount":13,"unpackedSize":62366,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDj82Lqoiy+p2GPfE4BzQ8eIj9Kk+M6G1frXutce8nOTAiAc0KfXmIGDHV6uGInWj2hRAYat6vuNQzcx2zb62nAlxQ=="}]},"_npmUser":{"name":"aadi49","email":"sharmaaaditya142@gmail.com"},"directories":{},"maintainers":[{"name":"aadi49","email":"sharmaaaditya142@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/preflight_0.1.0_1787038836617_0.4285651664987318"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-18T07:40:36.451Z","0.1.0":"2026-08-18T07:40:36.776Z","modified":"2026-08-18T07:40:36.962Z"},"maintainers":[{"name":"aadi49","email":"sharmaaaditya142@gmail.com"}],"description":"See what will actually break before you upgrade an npm dependency.","homepage":"https://preflight-umber.vercel.app","keywords":["npm","upgrade","dependencies","breaking-changes","static-analysis"],"repository":{"type":"git","url":"git+https://github.com/AadiSharma49/preflight.git"},"bugs":{"url":"https://github.com/AadiSharma49/preflight/issues"},"license":"MIT","readme":"# preflight\r\n\r\nSee what will actually break before you upgrade an npm dependency.\r\n\r\n**[preflight-umber.vercel.app](https://preflight-umber.vercel.app)** · [source](https://github.com/AadiSharma49/preflight)\r\n\r\nToday, \"is this upgrade safe?\" means a human reads a changelog, guesses which of\r\ntheir files are affected, and finds out for real in production. preflight\r\nreplaces the guessing: it scans your actual code for actual usage of the\r\npackage, cross-references the actual release notes between your current version\r\nand the target, and tells you which lines are affected and why.\r\n\r\n## Status\r\n\r\nStep 6 of 6 — the full pipeline is built: it finds real usage of a package in a\r\nreal repo, works out which version you're actually on, pulls the release notes\r\nfor every version between that and your target, matches your usage against the\r\nchangelog, checks transitive dependencies too, and prints one consolidated\r\nplain-text report with an exit code a CI check can read.\r\n\r\n- [x] 1. CLI scaffold + arg parsing\r\n- [x] 2. AST usage scanner (`@babel/parser`) — every import, file, line, named export\r\n- [x] 3. Current version from the lockfile + changelog fetch (GitHub releases, with a `CHANGELOG.md` fallback)\r\n- [x] 4. Match usage → changelog, split into **certain break** vs **maybe affected**\r\n- [x] 5. Transitive deps from the lockfile, not `package.json`\r\n- [x] 6. Plain grouped output — one report, certain first, with an exit code\r\n\r\n## GitHub Action\r\n\r\npreflight ships as a reusable GitHub Action. On every pull request it reads the\r\n`package.json` diff, runs preflight against each dependency whose range\r\nchanged, posts one consolidated PR comment (certain breaks first, maybes\r\nafter), and fails the check if any certain break is found. If the PR no longer\r\nchanges any dependency, any previous preflight comment is replaced with an\r\nall-clear rather than left stale.\r\n\r\n```yaml\r\n# .github/workflows/preflight.yml\r\nname: preflight\r\n\r\non:\r\n  pull_request:\r\n\r\npermissions:\r\n  contents: read\r\n  pull-requests: write\r\n\r\njobs:\r\n  preflight:\r\n    runs-on: ubuntu-latest\r\n    steps:\r\n      - uses: actions/checkout@v4\r\n\r\n      - uses: AadiSharma49/preflight@main\r\n        with:\r\n          token: ${{ github.token }}\r\n```\r\n\r\nThe action needs a token with `pull-requests: write` — the automatic\r\n`GITHUB_TOKEN` works with the `permissions` block above. If the token lacks\r\nthis permission, the action fails loudly with instructions rather than\r\nsilently skipping.\r\n\r\n### The action is bundled\r\n\r\nGitHub Actions does not run `npm install` for JavaScript actions, so the\r\naction entry and the whole CLI are bundled with esbuild into `dist/`\r\n(`dist/action.mjs` and `dist/cli.mjs`) and committed. Both bundles are\r\nself-contained — no `node_modules` is needed at runtime.\r\n\r\nAfter changing anything under `src/`, `bin/`, or `action/`, rebuild and commit\r\nthe bundles:\r\n\r\n```sh\r\nnpm run build:action   # writes dist/action.mjs and dist/cli.mjs\r\ngit add dist action.yml\r\n```\r\n\r\n## Install\r\n\r\n```sh\nnpm install -g @aadi49/preflight\npreflight react 19\n```\n\r\nThe installed command is `preflight` (from the `bin` field), not the scoped\r\npackage name.\r\n\r\n### Local dev\r\n\r\n```sh\r\nnpm link          # from this folder, once\r\npreflight react 19\r\n```\r\n\r\nTo unlink later: `npm unlink -g preflight`.\r\n\r\n## Usage\r\n\r\n```\r\npreflight <package> <target-version> [options]\r\n\r\n  -c, --cwd <path>   Repo to scan (default: current directory)\r\n      --json         Machine-readable output\r\n  -h, --help         Show help\r\n  -v, --version      Print preflight's own version\r\n```\r\n\r\nExamples:\r\n\r\n```sh\r\npreflight react 19\r\npreflight lodash 4.17.21\r\npreflight @tanstack/react-query ^5.0.0\r\npreflight axios latest --cwd ../relayos\r\n```\r\n\r\n## Layout\r\n\r\n```\r\nbin/cli.js        shebang + error boundary, nothing else\r\nsrc/cli.js        arg parsing, validation, report rendering\r\nsrc/walk.js       file discovery — honours .gitignore, skips build output\r\nsrc/scanner.js    the AST scanner\r\nsrc/installed.js  what version is actually installed\r\nsrc/registry.js   npm registry metadata -> the GitHub repo\r\nsrc/releases.js   GitHub Releases API + release-tag parsing\r\nsrc/changelog.js  resolves the target, computes the version range, fetches notes\r\nsrc/match.js      usage -> changelog matching, certain vs maybe\r\nsrc/transitive.js direct vs transitive from the lockfile\r\ntest/             one fixture per way the naive approach gets it wrong\r\nsite/             the landing page (Next.js, deploys separately)\r\n```\r\n\r\nThe `site/` folder is a standalone Next.js app and is not part of the published\r\nCLI package. On Vercel, set the project's Root Directory to `site`.\r\n\r\n## How the scanner works\r\n\r\nTwo passes, because \"the package is imported here\" is not useful — \"this\r\nspecific export is called on this line\" is.\r\n\r\n1. Find every import / `require` / dynamic import / re-export whose specifier\r\n   resolves to the package. `framer-motion/dom` matches; `framer-motion-3d`\r\n   does not.\r\n2. For each one, resolve the **binding** and walk every reference to it. Using\r\n   Babel's scope resolution rather than name matching is what makes aliases\r\n   (`import { useScroll as s }`) report the real export name, and what makes a\r\n   local variable that shadows the import correctly *not* count.\r\n\r\nA third pass handles TypeScript type positions, because Babel's scope tracks\r\nvalue references only — without it, `import type { Variants }` would show the\r\nimport line and none of the places the type is actually used.\r\n\r\nComments and strings that look like imports are free: the AST never sees them.\r\n\r\n## How the changelog fetch works\r\n\r\n`preflight framer-motion 12` only names the target. The current version is\r\nread from the repo, most-truthful source first: `package-lock.json`, then\r\n`yarn.lock`, then `pnpm-lock.yaml`, then `node_modules`, and only then the\r\nrange in `package.json` — which is flagged as inexact, because `^12.0.0` is\r\nwhat was *asked for*, not what is installed.\r\n\r\nThe target is then resolved against the registry (`12` → the newest published\r\n`12.x`), the range is every published version in `(current, target]`, and\r\nrelease notes are fetched for each.\r\n\r\nMatching a GitHub release to a version is less obvious than it looks. Four\r\nconventions are in use across ordinary dependencies:\r\n\r\n```\r\nv1.3.25               lenis, next\r\n7.9.1                 prisma\r\n@clerk/nextjs@7.5.2   clerk — one monorepo, one tag per package\r\nframer-motion@12.0.0  monorepo, unscoped\r\n```\r\n\r\nThe monorepo case is the one that matters: in `clerk/javascript`, a tag of\r\n`@clerk/vue@2.4.22` must not be read as version 2.4.22 of `@clerk/nextjs`, or\r\nyou attach the wrong changelog to the wrong upgrade.\r\n\r\nNot every project publishes GitHub releases at all — `framer-motion` has none,\r\nit keeps a `CHANGELOG.md`. When the Releases API comes up short, the changelog\r\nfile is fetched from `raw.githubusercontent.com` and split by version heading.\r\nBoth common formats are handled:\r\n\r\n```\r\n## [12.43.0] 2026-07-27    Keep a Changelog\r\n## 7.6.4                   changesets\r\n```\r\n\r\nPackage-specific paths (`packages/nextjs/CHANGELOG.md`) are tried before the\r\nrepo root, because in a monorepo the root file is another package's history.\r\n\r\nFetching from `raw.githubusercontent.com` rather than the API is deliberate: it\r\ndoes not count against the 60/hour unauthenticated rate limit, so the fallback\r\nstill works in the exact situation where the API has run out.\r\n\r\nIf neither source has anything — no repository field, or no notes anywhere —\r\nthat is reported plainly rather than shown as an all-clear.\r\n\r\nRun `npm test` to see every case that's covered.\r\n\r\n## A note on the npm name\r\n\r\n`preflight` is already taken on the public registry, so this package is\npublished as `@aadi49/preflight`. The installed command is still\n`preflight` because that comes from the `bin` field, not the package name.\n\r\n## Requirements\r\n\r\nNode >= 18.3 (uses the built-in `util.parseArgs`). Runtime dependencies:\r\n`@babel/parser`, `@babel/traverse`, `ignore`.\r\n","readmeFilename":"README.md","_rev":"1-622665ed4f80d0e1c435c9ae01ccce64"}