feat(cli): expose programmatic Node install API#479
Merged
Conversation
Add @react-grab/cli/api entry so external tools can build their own installers around React Grab without shelling out to the CLI. - High-level installReactGrab() orchestrator (detect + install + transform) with structured result and ReactGrabInstallError codes - Re-export low-level primitives (detect*, previewTransform, applyTransform, installPackages, installSkill/removeSkill) and their types - Non-interactive installSkill() extracted from promptSkillInstall - Export ProjectInfo, add ./api subpath export and api build entry - Document the API and add tests Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
commit: |
- Derive Next.js router type when framework is overridden to next, so the Pages-router path is not wrongly chosen - Pin cwd/packageManager over installPackageOptions and Omit them from the option type so the install dir cannot diverge from the edited file - Wrap package-manager failures in a ReactGrabInstallError (install-failed, preserves error.cause) so the structured error contract covers the most likely runtime failure - Skip the transform-failed throw when skipTransform is set - Resolve cwd to an absolute path; reuse getPackagesToInstall() - Document accurate force semantics, monorepo/cwd expectations, and the mutating nature of the call - Add tests for router derivation, skip flags, option precedence, and install-failed; type the project fixture as ProjectInfo Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Importing @react-grab/cli/api dragged in the interactive promptSkillInstall (and its prompts/ora deps) because it lived in install-skill.ts, which the API re-exports for installSkill/removeSkill. - Move promptSkillInstall into utils/prompt-skill-install.ts; install-skill.ts now only carries the lean installSkill/removeSkill + light deps - Point the init/add commands at the new module - Align installReactGrab's pending-change predicate with init's invariant (originalContent + newContent + !noChanges) to prevent drift - Fix the README low-level example to guard writes on !noChanges and reuse getPackagesToInstall() Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
4 issues found across 11 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
- Drop the originalContent truthiness check in installReactGrab so an empty source file still receives the injected setup (matches applyTransform's own write guard) - Use process.cwd() in the README low-level example so the snippet is valid when copied as-is - Stop exporting the internal detectMonorepo helper from the public API surface (detectProject already returns isMonorepo) Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Reflect that cwd/packageManager are excluded (Omit) since the orchestrator controls them. Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
- Active voice in the section opener and orchestrator summary - Convert list-shaped prose (error codes, export surface) into lists - Split the over-long low-level snippet into two blocks with prose between, moving inline code comments into prose - Split a four-idea paragraph into scope and mutation paragraphs Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
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.
Summary
Today, all of React Grab's install/setup logic is locked behind the CLI: importing
@react-grab/cliimmediately parsesargvand runs the program, and there are no subpath exports. There's no way for an external tool to detect a project, install the package, and apply the framework setup programmatically.This PR adds a side-effect-free Node API at
@react-grab/cli/apiso external consumers can build their own installers/CLIs around React Grab.Answer to "does it expose a Node API?"
It did not. Now it does — via
@react-grab/cli/api.What's added
installReactGrab(options?)— a high-level, non-interactive orchestrator that detects the project, installs thereact-grabpackage with the detected package manager, and applies the framework-specific dev-only setup. Returns a structuredInstallReactGrabResultinstead of printing/exiting. Supports overrides (framework,nextRouterType,packageManager), andforce/skipPackageInstall/skipTransform/dryRunflags.ReactGrabInstallErrorwith a discriminatedcode(unsupported-framework,unknown-framework,transform-failed,install-failed,write-failed) so callers can branch on failure cause instead of parsing messages or hittingprocess.exit. The underlying error is preserved onerror.cause.detectProject,detectFramework,detectPackageManager,detectNextRouterType,detectReactGrab,detectReactGrabConfigured,detectUnsupportedFramework,findReactProjects,previewTransform,previewOptionsTransform,previewCdnTransform,applyTransform,hasFrameworkEntryPoint,installPackages,getPackagesToInstall,installSkill,removeSkill— plus all relevant types.installSkill(options?)— a non-interactive agent-skill install extracted frompromptSkillInstall(which now reuses it; no behavior change to the interactive flow).Implementation notes
packages/cli/src/api.ts(no side effects on import) — distinct from the existing CLI entry.which still parsesargv.packages/cli/src/utils/install-react-grab.tshouses the orchestrator + error type.ProjectInfois now exported fromdetect.tsfor typed consumers.package.jsongains an./apisubpath export (types/import/require);vite.config.tsbuildssrc/api.tsalongsidesrc/cli.ts. The existing.export and bins are untouched.Usage
Or compose the primitives directly (
detectProject→previewTransform→installPackages→applyTransform→installSkill). See the updatedpackages/cli/README.mdfor the full table and examples.installReactGrabtargets a single project atcwd(usefindReactProjectsto locate apps in a monorepo first), and mutates the project unlessdryRun: true.Review hardening
A second pass (code-quality, performance, reuse, and security review) tightened the orchestrator:
frameworkis overridden tonext, so the Pages-router path isn't wrongly selected.cwd/packageManageroverinstallPackageOptions(andOmitthem from its type) so the install directory can't diverge from the edited file.install-failed(preservingerror.cause) so the structured error contract covers the most likely runtime failure.transform-failedwhenskipTransformis set; resolvecwdto an absolute path; reusegetPackagesToInstall(); align the pending-change predicate withinit's invariant.promptSkillInstall(and itsprompts/oraimports) moved toutils/prompt-skill-install.ts, soimport "@react-grab/cli/api"no longer eagerly loads the interactive prompt/spinner stack. Verified the interactive code is absent from the builtapigraph.forcesemantics, the monorepo/cwd+ mutation expectations, and corrected the low-level README example.Deferred (noted, out of scope)
initcommand to delegate toinstallReactGrab—initinterleaves an interactive diff/confirm between preview and apply and uses spinner-wrapped install/apply, so delegating would risk the CLI UX. The shared gating predicates (getPackagesToInstall,!hasReactGrab, the pending-change check) are now aligned to minimize drift.findReactProjects).Testing
pnpm --filter @react-grab/cli build— bothcliandapientries build,.d.tsgenerated.import/requireof the builtapiexpose the full surface and that the interactive prompt/spinner code is no longer in theapigraph.pnpm test— 225 passing (test/install-react-grab.test.tscovers fresh install, already-installed, dry-run, no-op, framework/router + package-manager overrides, skip flags,installPackageOptionsprecedence, and every error code).lint+formatclean (run under Node 22.18, since the repo'svite.config.tsconfig loader requires Node ≥22.18 for default TS stripping).Summary by cubic
Adds a side-effect-free Node API at
@react-grab/cli/apito detect, install, and configurereact-grabprogrammatically with structured results and errors. Keeps the API bundle lean by moving interactive prompts out and tightens the Node API README.New Features
installReactGrab(options?): non-interactive detect/install/transform orchestrator with overrides and flags (force,skipPackageInstall,skipTransform,dryRun); returns a structured result.installPackageOptionsexcludescwd/packageManager(set by the orchestrator).ReactGrabInstallErrorwith codes:unsupported-framework,unknown-framework,transform-failed,install-failed,write-failed.detectProject,previewTransform,applyTransform,installPackages,getPackagesToInstall,installSkill,removeSkill,ProjectInfo, etc.). New./apisubpath and build entry;src/api.tshas no import side effects. README docs and tests added.Refactors
promptSkillInstalltoutils/prompt-skill-install.ts; API now exports leaninstallSkill/removeSkill(non-interactive).init/addupdated;removenow logs per-agent andremoveSkillreturns the removed agents.applyTransform: droporiginalContentcheck so empty files still receive setup. README examples useprocess.cwd(), guard on!noChanges, reusegetPackagesToInstall(), and document preciseinstallPackageOptionstype.framework: "next"is overridden, pincwd/packageManager, wrap package-manager failures asinstall-failed, skip throwing on failed preview whenskipTransformis set, and resolvecwdto an absolute path.detectMonorepofrom the public API.Written for commit fc377ce. Summary will update on new commits.