feat(vfs-bundle): detect the package manager from its lockfile; .sol default entries for Soldeer - #200
Merged
Conversation
exo-nikita
force-pushed
the
claude/affectionate-ritchie-1j9umy
branch
from
October 2, 2026 06:36
dd9e5f3 to
5962d2b
Compare
exo-nikita
force-pushed
the
claude/affectionate-ritchie-1j9umy
branch
2 times, most recently
from
October 2, 2026 08:40
44a6ea1 to
1aa6769
Compare
exo-nikita
force-pushed
the
claude/affectionate-ritchie-1j9umy
branch
4 times, most recently
from
October 2, 2026 09:23
52ccacf to
bd34faf
Compare
…default entries for Soldeer buildVfsBundle, buildGitHubBundle, suggestedEntries and `stasis github-bundle` take `packageManager` / `--package-manager` as optional, and so does loadNodeModules. Without one, each supported package manager is checked for a lockfile in the directory it would install cwd from, as lockfileRoot finds that directory: - pnpm: the workspace root, else the nearest pnpm-lock.yaml. - yarn 1: the root that declares cwd's workspace, else the nearest yarn.lock. - Soldeer: the nearest foundry.toml or soldeer.toml, never above the git root. If exactly one package manager's lockfile is there, it is taken. If none or more than one is, the call is refused, and the error names the lockfiles that were looked for, or the ones found with their package managers. buildVfsBundle chooses among all three. loadNodeModules chooses between pnpm and yarn 1, so a soldeer.lock beside them is not counted. A package manager that is given is always taken, whatever lockfiles are present. buildGitHubBundle (and suggestedEntries for a repo) reads the repo listings first: the directory's and those above it, listed once and shared with the subtree check. - No supported lockfile listed: refused before anything is downloaded. - One listed: that package manager is the only one that can install the directory, so it is taken. The directory is still downloaded on its own where it stands alone. - More than one listed, or a directory no listing covers (a symlink): the whole repo is downloaded and checked. In every case the choice is confirmed against the tree that was downloaded. Option checks that need the package manager's kind run as soon as it is known; before that, given entries are checked to be of a kind some supported package manager builds. buildVfsBundle returns the `packageManager` it built with. Default entries follow the package manager's kind. With pnpm and yarn 1 they remain the package.json's entry points. With Soldeer they are the project's .sol entry points, picked by name and layout as preventive/triage picks them: - .sol files directly in the directory; - .sol files under contracts/; - .sol files under its source directory: the `src` of foundry.toml's [profile.default], else src/ (forge's default, or contracts/ when only that exists). Triage takes src/ only beside a foundry.toml or a hardhat.config.*, to tell a Solidity project's src/ from a JS one's. Here the package manager is Soldeer, which already says that, and Hardhat projects are pnpm or yarn ones, so src/ is always taken. No profile but the default one is read, and nothing from the environment. Tests, scripts and mocks are left out, by directory name and by *.t.sol, *.s.sol, *.test.sol and *.spec.sol. So is anything a project depends on or builds: lib/, node_modules/, dependencies/, out/, cache/, artifacts/ and build/. Directories no stasis walk descends into (isAutoExcludedDir: VCS metadata, examples, test scaffolding) are skipped too. A link to a directory is not followed. Soldeer no longer needs its entries given. suggestedEntries takes `packageManager`, and suggests exactly the build's default entries. Without a package manager, it detects one as the build does, so it needs the lockfile the build needs. For a repo, it downloads the tree the way buildGitHubBundle does, through the same code. Options are checked before anything is fetched: - Once the package manager is known, its kind's checks run. - With neither entries nor a package manager, an option no kind takes is refused (e.g. --tsconfig without --typescript). - A `packageManagerVersion` without a `packageManager` is refused. - `stasis github-bundle` hands `--lockfile` to these checks, so it is refused for a Solidity bundle as soon as the kind is known: from `.sol` entries before anything is fetched, or once the listings or the tree tell Soldeer. Listing details: - A directory's tree id is taken from the listing above it, which upstream holds to that directory's own id. This saves a walk from the commit for each subtree. - A failed root listing is reported, not taken for a symlink's. Nothing in these builds follows the environment. buildVfsBundle (and so buildGitHubBundle) no longer takes an `env`: a Solidity bundle is built with foundry.toml's default profile, whatever FOUNDRY_PROFILE, FOUNDRY_REMAPPINGS or DAPP_REMAPPINGS say. `stasis github-bundle` reads only GITHUB_TOKEN. Detection resolves a symlinked cwd first in loadNodeModules, as buildVfsBundle does. foundry.js gains foundrySourceDir. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KPSLumcb6Rdo4xXL5s5CpE
exo-nikita
force-pushed
the
claude/affectionate-ritchie-1j9umy
branch
from
October 2, 2026 09:43
bd34faf to
d02acff
Compare
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.
packageManager/--package-manageris now optional for:buildVfsBundle;buildGitHubBundle;suggestedEntries;stasis github-bundle;loadNodeModules.Default entries now follow the package manager's kind; for Soldeer they are the project's
.solentry points. Nothing in these builds follows the environment.Detection
Without a package manager given, each supported one is checked for a lockfile in the directory it would install
cwdfrom. That directory is found the same waylockfileRootfinds it:pnpm-lock.yamlcwd's workspace, else the nearestyarn.lockfoundry.toml/soldeer.toml, never above the git rootIf exactly one package manager's lockfile is there, it is taken. If none or more than one is, the call is refused, and the error names what was looked for or found:
buildVfsBundle: chooses among all three, and returns thepackageManagerit built with.loadNodeModules: chooses between pnpm and yarn 1, resolving a symlinkedcwdfirst.packageManagerVersionwithout one is refused.For a GitHub repo (
buildGitHubBundle,suggestedEntries), the repo listings are read first: the directory's and those above it, each listed once.Option checks run before anything is fetched:
--tsconfigwithout--typescript).Default entries
pnpm, yarn 1: the
package.json's entry points, as before.Soldeer:
.solentry points by name and layout, following preventive/triage:.solfiles directly in the directory;.solfiles undercontracts/;.solfiles under its source directory: thesrcoffoundry.toml's[profile.default], elsesrc/.Left out:
test(s),script(s),mock(s)) and by*.t.sol,*.s.sol,*.test.sol,*.spec.sol;lib/,node_modules/,dependencies/,out/,cache/,artifacts/,build/;isAutoExcludedDir): VCS metadata, examples, test scaffolding.A link to a directory is not followed. Triage takes
src/only beside afoundry.tomlorhardhat.config.*, to tell a Soliditysrc/from a JS one. Here the package manager being Soldeer already says that, and Hardhat projects are pnpm or yarn ones.suggestedEntriestakespackageManagerand suggests exactly the build's default entries. Without a package manager, it detects one as the build does. For a repo, it downloads the tree the waybuildGitHubBundledoes, through the same code.Environment
buildVfsBundle(and sobuildGitHubBundle) no longer takes anenv. A Solidity bundle is built withfoundry.toml's default profile, whateverFOUNDRY_PROFILE,FOUNDRY_REMAPPINGSorDAPP_REMAPPINGSsay.stasis github-bundlereads onlyGITHUB_TOKEN. It hands--lockfileto the build's option checks, so--lockfileis refused for a Solidity bundle as soon as the kind is known:.solentries, before anything is fetched;Other changes
repoTree: does the shared GitHub setup (repo checks, client, head commit, option checks, download, detection) forbuildGitHubBundleandsuggestedEntries.checkAheadandKINDS: one up-front check, and a per-kind table holding the stand-in entry, the default entries and the "none found" message.checkKind: refuses a kind no package manager builds.checkVfsOptionsjust returns the kind.packageManagerFor: the given-or-detected package manager, shared bybuildVfsBundleandloadNodeModules.foundry.js: gainsfoundrySourceDir.Tests
tests/vfs-bundle-detect.test.js(new):loadNodeModuleschoosing between pnpm and yarn 1, and through a symlinkedcwd;packageManagerVersionwithoutpackageManager.tests/vfs-bundle-soldeer.test.js:yarn.lock;.solentries building the bundlesrc/does;envignored:FOUNDRY_PROFILE,FOUNDRY_REMAPPINGSandDAPP_REMAPPINGSchange nothing.tests/vfs-bundle-github.test.js:.solentries: what is included, what is skipped, thefoundry.tomlsrc, a source directory outside the project, a linkedcontracts/, and no entries refused;suggestedEntrieswith a given or detected package manager, matchingbuildGitHubBundle's defaults;--lockfilewith Soldeer;FOUNDRY_PROFILEnot reachingstasis github-bundle.Rebased onto main (#202).
node --run lintis clean andnode --run testpasses: 2204 passed, 3 skipped.🤖 Generated with Claude Code
https://claude.ai/code/session_01KPSLumcb6Rdo4xXL5s5CpE