Found while evaluating 0.24.0-rc.3 in an adopter project. Adjacent to #325 but a different code path — see "Not a duplicate of #325" below.
Summary
Ecosystem detection is root-only:
has_npm() { [ -f "$PROJECT/package.json" ]; }
In a repo whose root is not a JS project — a Maven- or pip-rooted monorepo with JS apps in sub-directories — this returns false, so link skips npm entirely: no .npmrc, no repin, no lockfile handling, and npm is not listed in the detected ecosystems.
link then reports success. Every @metaobjectsdev/* dependency silently stays on the previously-released version, so the adopter believes they are testing the RC on the TS side when they are not — a false green, which is worse for a pre-release evaluation than a hard failure.
Observed
Repo root has pom.xml and no package.json; three independent JS install roots underneath, each with its own bun.lock, collectively depending on 16 @metaobjectsdev/* packages.
$ prerelease-link.sh link --version 0.24.0-rc.3
── linking <repo-root> → <registry> (owner <owner>) ──
pinning vendor dependencies to 0.24.0-rc.3
repinned com.metaobjects dependencies to 7.24.0-rc.3 (via <metaobjects.version>)
✓ linked. Install/restore, then iterate:
maven mvn -U compile
Only maven is listed. All 16 npm packages remain on the prior minor.
The tell
repin_npm() already handles this correctly — it does not use $PROJECT/package.json, it recurses:
find "$PROJECT" -name package.json -not -path '*/node_modules/*' ...
So the repin logic is nested-aware and only the gate in front of it is root-only. Running link --project <sub> against each JS root individually works perfectly and pins all 16.
Not a duplicate of #325
#325 is the case where npm is detected (root package.json exists) and sub-projects get repinned without receiving an .npmrc. Here npm is never detected at all, so nothing is repinned and no .npmrc is written anywhere — a silent no-op rather than a notarget install failure. Different cause (has_npm gate vs link_npm write scope), different symptom, and #325's fix (write an .npmrc per changed manifest) does not help if the gate never opens.
The two together suggest the underlying assumption worth revisiting: "the project has one root, and it is $PROJECT."
Suggested fix
Make detection match the recursion that already exists downstream — detect npm if any non-ignored package.json is found, using the same find and prune rules as repin_npm. The same argument applies to has_py (root-only pyproject.toml / requirements*.txt); has_nuget already recurses via find, so it is the odd one out in the right direction.
Workaround for adopters today
Run link once per JS root:
for p in <app> <console> <scripts/docs>; do
prerelease-link.sh link --version 0.24.0-rc.3 --project "$p"
done
Each run correctly writes .npmrc, repins that manifest, and adds the local git exclude. Remember that unlink must then also be run once per root.
Found while evaluating
0.24.0-rc.3in an adopter project. Adjacent to #325 but a different code path — see "Not a duplicate of #325" below.Summary
Ecosystem detection is root-only:
In a repo whose root is not a JS project — a Maven- or pip-rooted monorepo with JS apps in sub-directories — this returns false, so
linkskips npm entirely: no.npmrc, no repin, no lockfile handling, and npm is not listed in the detected ecosystems.linkthen reports success. Every@metaobjectsdev/*dependency silently stays on the previously-released version, so the adopter believes they are testing the RC on the TS side when they are not — a false green, which is worse for a pre-release evaluation than a hard failure.Observed
Repo root has
pom.xmland nopackage.json; three independent JS install roots underneath, each with its ownbun.lock, collectively depending on 16@metaobjectsdev/*packages.Only
mavenis listed. All 16 npm packages remain on the prior minor.The tell
repin_npm()already handles this correctly — it does not use$PROJECT/package.json, it recurses:So the repin logic is nested-aware and only the gate in front of it is root-only. Running
link --project <sub>against each JS root individually works perfectly and pins all 16.Not a duplicate of #325
#325 is the case where npm is detected (root
package.jsonexists) and sub-projects get repinned without receiving an.npmrc. Here npm is never detected at all, so nothing is repinned and no.npmrcis written anywhere — a silent no-op rather than anotargetinstall failure. Different cause (has_npmgate vslink_npmwrite scope), different symptom, and #325's fix (write an.npmrcper changed manifest) does not help if the gate never opens.The two together suggest the underlying assumption worth revisiting: "the project has one root, and it is
$PROJECT."Suggested fix
Make detection match the recursion that already exists downstream — detect npm if any non-ignored
package.jsonis found, using the samefindand prune rules asrepin_npm. The same argument applies tohas_py(root-onlypyproject.toml/requirements*.txt);has_nugetalready recurses viafind, so it is the odd one out in the right direction.Workaround for adopters today
Run
linkonce per JS root:Each run correctly writes
.npmrc, repins that manifest, and adds the local git exclude. Remember thatunlinkmust then also be run once per root.