Package & version
@rnx-kit/third-party-notices@2.0.2 (latest at time of filing).
Repro
- Yarn 4 with
nodeLinker: pnpm (workspace layout: node_modules/.store/<pkg>/package/...).
- A workspace whose dependency graph includes modern dual-format ESM/CJS packages — e.g.
socket.io-client@4.x, @socket.io/component-emitter
- Run a Metro bundle that invokes
third-party-notices (e.g. via @office-iss/sdx-build-tools).
Root cause
Modern dual-format packages ship subpath package.json markers to flag a subdirectory's module format, e.g.:
// .../socket.io-client/build/esm/package.json
{ "name": "socket.io-client", "version": "4.8.3", "type": "module" }
These are not package boundaries — they lack license, dependencies, etc. Under the Yarn 4 pnpm linker, source paths resolved by Metro often point into such subdirectories, so splitSourcePath in lib/write-third-party-notices.js calls findPackageDir(p) and lands on the marker rather than the real package root.
Three crash sites (all from the same root cause)
lib/write-third-party-notices.js:165 — parseModule does moduleName.startsWith(...), but moduleName is undefined when the marker has no name.
lib/extractors.js:45 — module.version.match(...) throws because the marker has no version.
lib/output/text.js:17 — throw new Error(\"No license information found...\") fires because the marker has no license, even though the real package root one directory up does.
Proposed fix
In splitSourcePath, walk up past subpath markers when the discovered package.json lacks license/dependencies (heuristic for "this is a subpath marker, not a real package boundary"):
let pkgRoot = findPackageDir(p);
while (pkgRoot) {
const manifest = readPackage(pkgRoot);
if (manifest && (manifest.license || manifest.licenses ||
manifest.dependencies || manifest.peerDependencies)) {
return [manifest.name, pkgRoot];
}
const parent = path.dirname(pkgRoot);
if (parent === pkgRoot) break;
const next = findPackageDir(parent);
if (!next || next === pkgRoot) break;
pkgRoot = next;
}
Validated locally in our internal monorepo — with the walk-up, the bundle build passes cleanly with no ignoreModules workarounds; affected packages resolve to their real roots and emit correct license entries.
Filing as an issue (not a PR) so maintainers can choose their preferred approach — the snippet above is just a starting point.
Package & version
@rnx-kit/third-party-notices@2.0.2(latest at time of filing).Repro
nodeLinker: pnpm(workspace layout:node_modules/.store/<pkg>/package/...).socket.io-client@4.x,@socket.io/component-emitterthird-party-notices(e.g. via@office-iss/sdx-build-tools).Root cause
Modern dual-format packages ship subpath
package.jsonmarkers to flag a subdirectory's module format, e.g.:These are not package boundaries — they lack
license,dependencies, etc. Under the Yarn 4 pnpm linker, source paths resolved by Metro often point into such subdirectories, sosplitSourcePathinlib/write-third-party-notices.jscallsfindPackageDir(p)and lands on the marker rather than the real package root.Three crash sites (all from the same root cause)
lib/write-third-party-notices.js:165—parseModuledoesmoduleName.startsWith(...), butmoduleNameisundefinedwhen the marker has noname.lib/extractors.js:45—module.version.match(...)throws because the marker has noversion.lib/output/text.js:17—throw new Error(\"No license information found...\")fires because the marker has nolicense, even though the real package root one directory up does.Proposed fix
In
splitSourcePath, walk up past subpath markers when the discoveredpackage.jsonlacks license/dependencies (heuristic for "this is a subpath marker, not a real package boundary"):Validated locally in our internal monorepo — with the walk-up, the bundle build passes cleanly with no
ignoreModulesworkarounds; affected packages resolve to their real roots and emit correct license entries.Filing as an issue (not a PR) so maintainers can choose their preferred approach — the snippet above is just a starting point.