Skip to content

feat: add script for local dependency linking#899

Open
troykessler wants to merge 7 commits intomainfrom
feat/local-linking
Open

feat: add script for local dependency linking#899
troykessler wants to merge 7 commits intomainfrom
feat/local-linking

Conversation

@troykessler
Copy link
Contributor

@troykessler troykessler commented Jan 16, 2026

This PR introduces a script to locally link packages from the monorepo with pnpm pack. It allows local development again after pnpm link did not work anymore after the pnpm catalog has been introduced on the monorepo

Summary by CodeRabbit

  • New Features

    • Added link/unlink scripts to streamline using local monorepo packages in development.
  • Documentation

    • Added detailed developer docs and a new subsection with quick-start commands, usage, workflow, and troubleshooting for monorepo linking.
  • Chores

    • Updated ignore patterns to exclude local monorepo tarball artifacts from version control.

✏️ Tip: You can customize this high-level summary in your review settings.

@troykessler troykessler self-assigned this Jan 16, 2026
@vercel
Copy link

vercel bot commented Jan 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
hyperlane-warp-template Ready Ready Preview, Comment Jan 20, 2026 9:01am
4 Skipped Deployments
Project Deployment Review Updated (UTC)
injective-bridge Ignored Ignored Jan 20, 2026 9:01am
nexus-bridge Ignored Ignored Jan 20, 2026 9:01am
ousdt-bridge Ignored Ignored Jan 20, 2026 9:01am
trump-bridge Ignored Ignored Jan 20, 2026 9:01am

Request Review

@socket-security
Copy link

socket-security bot commented Jan 16, 2026

No dependency changes detected. Learn more about Socket for GitHub.

👍 No dependency changes detected in pull request

@coderabbitai
Copy link

coderabbitai bot commented Jan 16, 2026

📝 Walkthrough

Walkthrough

Adds tooling, docs, and ignore rules to support packing and linking local hyperlane-monorepo packages: new link-monorepo.js and unlink-monorepo.js scripts, npm scripts, detailed docs, and .monorepo-tarballs ignored. No public API changes.

Changes

Cohort / File(s) Summary
Gitignore
.gitignore`
Adds .monorepo-tarballs to ignore locally-created monorepo tarballs.
Package configuration
package.json`
Adds link:monorepo and unlink:monorepo npm scripts (plus minor formatting).
Top-level docs
README.md`
Adds "Local package linking to hyperlane-monorepo" subsection with usage and commands for linking/unlinking.
Scripts docs
scripts/README.md`
New comprehensive documentation for link-monorepo.js and unlink-monorepo.js workflows, usage, troubleshooting, and examples.
Linking script
scripts/link-monorepo.js`
New script: builds monorepo, packs specified packages, copies tarballs into .monorepo-tarballs, updates app package.json deps to file: tarball refs, writes pnpm.overrides, cleans, and runs pnpm install.
Unlinking script
scripts/unlink-monorepo.js`
New script: detects file: tarball refs, removes related pnpm.overrides, queries npm for published versions, restores package.json deps, cleans, removes local tarballs, and reinstalls.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Script as link-monorepo.js
    participant Monorepo as Monorepo
    participant TarballDir as .monorepo-tarballs
    participant App as App (package.json)

    User->>Script: Run link:monorepo [packages]
    Script->>Monorepo: Verify monorepo exists & run pnpm build
    Script->>Monorepo: Run pnpm pack for each package
    Monorepo->>TarballDir: Produce .tgz files (copied into .monorepo-tarballs)
    Script->>App: Update dependencies to file:tarball paths
    Script->>App: Add/ensure pnpm.overrides for packed versions
    Script->>App: Remove node_modules & pnpm-lock.yaml
    Script->>App: Run pnpm install
    Script->>User: Print packed packages summary and guidance
Loading
sequenceDiagram
    actor User
    participant Script as unlink-monorepo.js
    participant App as App (package.json)
    participant Registry as npm Registry
    participant TarballDir as .monorepo-tarballs

    User->>Script: Run unlink:monorepo
    Script->>App: Scan dependencies for file:../ or file:.monorepo-tarballs references
    Script->>App: Remove corresponding pnpm.overrides
    Script->>Registry: npm view <pkg> version (for each package)
    Registry-->>Script: Return latest published versions
    Script->>App: Update dependencies to published versions
    Script->>App: Remove node_modules & pnpm-lock.yaml
    Script->>App: Run pnpm install
    Script->>TarballDir: Remove local tarballs directory
    Script->>User: Print restore summary and status
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • antigremlin

Poem

A wee tarball stash beneath the bog,
Scripts that pack, replace, then clear the log.
Link, test, unlink — keepin' dev snug and sly,
Spin the wheel, rebuild, and watch things fly.
Cheers to tidy local flows — ogre-approved.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add script for local dependency linking' directly and accurately summarizes the main change—introducing new scripts (link-monorepo.js and unlink-monorepo.js) to enable local package linking in the monorepo workflow.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/local-linking

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@package.json`:
- Line 117: The README for the linking script is missing the deploy-sdk package:
update scripts/README.md's "Common packages to link" section to include
deploy-sdk (the package referenced by the package.json script "link:monorepo"
which already lists deploy-sdk), so that the README lists "deploy-sdk" alongside
utils, deploy-sdk, sdk, widgets (or similar entries) and documents it as a
linkable package for developers.

In `@scripts/link-monorepo.js`:
- Around line 86-91: The script currently uses args.forEach to build pkgPath
with path.join(MONOREPO_PATH, 'typescript', folder) and then operates on that
path, allowing path traversal like "../foo"; fix this by resolving and
normalizing the target directory (use path.resolve or path.normalize) and
verifying it is contained within the intended base directory (compute a baseDir
= path.resolve(MONOREPO_PATH, 'typescript') and ensure path.relative(baseDir,
resolvedPkgPath) does not start with '..' and is not absolute), and if the check
fails, warn and return; apply this guard before any fs.existsSync checks or any
removal of .tgz files so no operations occur outside the monorepo/typescript
tree.

In `@scripts/README.md`:
- Around line 95-101: The README note is inaccurate: update the bullet note to
state that unlink-monorepo.js automatically fetches and restore published
versions using npm view (so manual package.json edits are usually unnecessary),
and add a brief clarification about any edge cases where manual intervention is
required (e.g., package not published or registry access errors); reference the
script name unlink-monorepo.js and its npm view behavior when editing the README
text.

In `@scripts/unlink-monorepo.js`:
- Around line 65-68: The script currently assigns the raw versionOutput from
execSync to packageJson.dependencies[name], losing any semver range prefix;
change the assignment in the unlink logic so packageJson.dependencies[name] is
set to a caret-prefixed semver string (e.g., '^' + versionOutput) instead of the
bare versionOutput, using the existing variables execSync, versionOutput and
name to locate the code and update the value.
- Around line 64-71: The current code calls execSync with an interpolated
package name (execSync(`npm view ${name} version`)), which allows command
injection; change it to a safe API call such as child_process.spawnSync or
execFileSync using an argument array (e.g., call spawnSync/execFileSync with
'npm' and ['view', name, 'version']) and read stdout into versionOutput, and/or
validate the package name against the npm package name pattern before use;
update the try block that sets packageJson.dependencies[name] and the console
log to use the sanitized/returned version and preserve the existing error
handling for the catch.
🧹 Nitpick comments (4)
README.md (1)

70-80: Consider polishing the prose a wee bit.

The documentation gets the job done, but the wording could be smoother. Something like this reads a bit easier on the eyes:

📝 Suggested wording improvement
-If you have to make changes to the widgets package to edit e.g. the Connect Button or other components linking
-the widgets package locally to test it is necessary. To do that you can run the following commands
+When making changes to the widgets package (e.g., the Connect Button or other components), you may need to link
+the package locally for testing. Run the following commands:
scripts/unlink-monorepo.js (1)

103-105: Consider adding a timeout for the install command.

This pnpm install could potentially hang forever if something goes sideways with the network or registry. For a development script it's not critical since developers can just hit Ctrl+C, but a timeout would make it more robust.

⏱️ Optional timeout
  execSync('pnpm install', {
-   stdio: 'inherit'
+   stdio: 'inherit',
+   timeout: 300000 // 5 minutes
  });
scripts/link-monorepo.js (2)

29-38: Drop or use the unused runSilent.
It’s dead code right now; either wire it up or remove it to keep the script tidy.

🧹 Cleanup option
-/**
- * Helper to run commands and capture output
- */
-function runSilent(command, cwd = REACT_APP_DIR) {
-  try {
-    return execSync(command, { cwd, encoding: 'utf8' }).trim();
-  } catch (err) {
-    return null;
-  }
-}

172-175: Add a loud reminder to undo file: deps before committing.
Since this script rewrites package.json, a friendly warning helps avoid accidental commits with local tarballs. Based on learnings, repo practice is to revert file: deps before merge.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 596a15c and 70d8ebb.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • .gitignore
  • README.md
  • package.json
  • scripts/README.md
  • scripts/link-monorepo.js
  • scripts/unlink-monorepo.js
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: Xaroz
Repo: hyperlane-xyz/hyperlane-warp-ui-template PR: 746
File: package.json:21-24
Timestamp: 2025-09-12T14:39:07.880Z
Learning: In the hyperlane-warp-ui-template repository, developers may temporarily use local file: paths in package.json to test against unpublished monorepo changes, but these should be reverted to published versions before merging.
Learnt from: paulbalaji
Repo: hyperlane-xyz/hyperlane-warp-ui-template PR: 831
File: .github/workflows/update-hyperlane-deps.yml:63-68
Timestamp: 2025-12-03T14:53:51.893Z
Learning: In the hyperlane-warp-ui-template repository, the automated dependency update workflow intentionally follows the same pattern as the registry workflow and does not include validation checks for file: paths in package.json, prioritizing consistency across workflows.
📚 Learning: 2025-09-12T14:39:07.880Z
Learnt from: Xaroz
Repo: hyperlane-xyz/hyperlane-warp-ui-template PR: 746
File: package.json:21-24
Timestamp: 2025-09-12T14:39:07.880Z
Learning: In the hyperlane-warp-ui-template repository, developers may temporarily use local file: paths in package.json to test against unpublished monorepo changes, but these should be reverted to published versions before merging.

Applied to files:

  • README.md
  • scripts/unlink-monorepo.js
  • scripts/README.md
  • scripts/link-monorepo.js
  • package.json
📚 Learning: 2025-12-03T14:53:51.893Z
Learnt from: paulbalaji
Repo: hyperlane-xyz/hyperlane-warp-ui-template PR: 831
File: .github/workflows/update-hyperlane-deps.yml:63-68
Timestamp: 2025-12-03T14:53:51.893Z
Learning: In the hyperlane-warp-ui-template repository, the automated dependency update workflow intentionally follows the same pattern as the registry workflow and does not include validation checks for file: paths in package.json, prioritizing consistency across workflows.

Applied to files:

  • README.md
  • scripts/README.md
  • scripts/link-monorepo.js
📚 Learning: 2025-12-17T22:19:35.975Z
Learnt from: CR
Repo: hyperlane-xyz/hyperlane-warp-ui-template PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T22:19:35.975Z
Learning: Use Next.js 15 with React 18 as the primary framework for this web application

Applied to files:

  • README.md
🧬 Code graph analysis (1)
scripts/link-monorepo.js (1)
scripts/unlink-monorepo.js (9)
  • require (1-1)
  • fs (2-2)
  • path (3-3)
  • REACT_APP_DIR (9-9)
  • LOCAL_TARBALLS_DIR (11-11)
  • packageJsonPath (10-10)
  • packageJson (17-17)
  • nodeModulesPath (82-82)
  • lockfilePath (83-83)
🪛 LanguageTool
README.md

[style] ~72-~72: Consider shortening or rephrasing this to strengthen your wording.
Context: ...g to hyperlane-monorepo If you have to make changes to the widgets package to edit e.g. the Co...

(MAKE_CHANGES)


[style] ~73-~73: Consider a more expressive alternative.
Context: ...age locally to test it is necessary. To do that you can run the following commands...

(DO_ACHIEVE)

🔇 Additional comments (4)
.gitignore (1)

40-40: LGTM!

This keeps the local tarball swamp out of version control, right where it belongs. The .monorepo-tarballs directory is a development-only artifact that shouldn't be wandering into the repository.

scripts/unlink-monorepo.js (1)

1-112: Overall structure looks solid.

The script handles the unlinking workflow well - finding file references, cleaning up overrides, restoring versions, and reinstalling. The logging is helpful and the error handling, while basic, gets the job done for a dev tool.

Based on learnings, this script properly supports the workflow where developers use local file: paths temporarily but need to revert to published versions before merging. Good stuff.

scripts/README.md (1)

142-161: Good reminder about testing with published versions.

Appreciate the note on line 148 about testing with published versions before releasing. That's wisdom right there - like remembering to check your swamp for surprises before inviting guests over. This aligns well with the repository's convention of reverting file: paths before merging PRs.

The comparison table with yalc is helpful for developers wondering why this approach was chosen.

scripts/link-monorepo.js (1)

127-133: [rewritten comment]
[classification tag]

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

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.

2 participants