Skip to content

@rnx-kit/third-party-notices crashes on subpath package.json markers (Yarn 4 pnpm linker, dual-format ESM/CJS packages) #4118

@Saadnajmi

Description

@Saadnajmi

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)

  1. lib/write-third-party-notices.js:165parseModule does moduleName.startsWith(...), but moduleName is undefined when the marker has no name.
  2. lib/extractors.js:45module.version.match(...) throws because the marker has no version.
  3. lib/output/text.js:17throw 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions