Skip to content

fix(engine): handle Yarn Berry workspace, patch and alias descriptors… - #516

Closed
Janil-ship-it wants to merge 0 commit into
vibgrate:mainfrom
Janil-ship-it:main
Closed

Janil-ship-it wants to merge 0 commit into
vibgrate:mainfrom
Janil-ship-it:main

Conversation

@Janil-ship-it

Copy link
Copy Markdown

fix(lockfile): skip workspace entries and resolve patch/alias descriptors in yarn.lock

Summary

Fixes Yarn Berry (v2+) yarn.lock parsing issues in src/engine/lockfile.ts:

  • Descriptor Resolution: Replaced lastIndexOf('@') splitting logic in headerNamesPackage with splitYarnDescriptor. This enables correct package name matching for patch: and alias: descriptors containing multiple @ characters (e.g., resolve@patch:... and string-width-cjs@npm:...), allowing lockfileVersion() to resolve their actual version strings.
  • Workspace Filtering: Updated yarnLockTree to skip __metadata blocks and entries where descriptor ranges start with workspace: (e.g., "my-app@workspace:."), preventing local project roots from being included as registry components in SBOM exports.
  • Testing: Added a describe('Yarn Berry lockfile') test suite in src/engine/lockfile.test.ts using Yarn Berry fixtures to verify workspace skipping, metadata filtering, and patch/alias version resolution.

Related issues

Closes #510

Checklist

  • pnpm test passes
  • pnpm lint is clean
  • pnpm typecheck is clean
  • Docs updated (README / DOCS / ARCHITECTURE) where behavior changed
  • Determinism preserved — identical input still produces identical graph.json / report output (content-hashed IDs, stable sorts; no time, randomness, or filesystem-order dependence)
  • No proprietary or internal references — public, Apache-2.0 content only
  • Commits use Conventional Commits and are signed off (git commit -s, DCO)

Notes for reviewers

  • Reused the existing splitYarnDescriptor helper function inside headerNamesPackage to maintain consistent descriptor parsing rules across the codebase.
  • Preserved full backward compatibility with classic Yarn v1 lockfiles; all existing test suites continue to pass without regression.

@vibgrate-team

Copy link
Copy Markdown
Contributor

Hi @Janil-ship-it, thanks for picking up #510, and sorry this sat for a few days without a review. I've approved the CI run for your branch now, so checks should report shortly.

Automated review: changes needed (one small fix)

The src/engine/lockfile.ts change looks right and follows the approach in the issue: headerNamesPackage now uses splitYarnDescriptor, and blocks whose descriptors are all workspace: ranges are skipped. With the test fixed (see below), the full suite passes on top of current main, and reverting either half of the fix makes the new test fail, so the test really does cover the behaviour. 👍

What's blocking right now is the new test itself:

  1. The test calls write() with the wrong arguments. The helper in src/engine/lockfile.test.ts is write(name: string, body: string): void, and it writes into the shared root from beforeEach. Passing it an object makes the test throw TypeError: The "path" argument must be of type string, and pnpm typecheck reports 4 errors (TS2554 / TS2345 at lines ~165, 197, 208 and 209). Please use the same form as the other tests:

    write(
      'yarn.lock',
      `
    __metadata:
      version: 8
    ...
    `.trimStart(),
    );
    
    const tree = fullDependencyTree(root);

    (so drop const root = …, since root is already in scope.)

  2. Small tidy-ups (non-blocking):

    • Please remove the two extra blank lines added before describe('lockfileVersion — npm precedence').
    • Please drop the // or string-width-cjs depending on yarnHeaderRealName comment. The behaviour is deterministic: the tree reports the real package string-width, while lockfileVersion(root, 'npm', 'string-width-cjs') looks up the alias.

Before you push, please run pnpm typecheck and pnpm test src/engine/lockfile.test.ts locally (the checklist says these passed, but on this branch they don't yet). Once that's done we'll take another look. Thanks again, this is a nice fix for Yarn Berry users.

@vibgrate-team vibgrate-team left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @Janil-ship-it, welcome, and thanks for your first contribution to Vibgrate! 🎉

Automated review: changes requested. The fix in src/engine/lockfile.ts looks right. Reusing splitYarnDescriptor in headerNamesPackage and skipping workspace: headers is exactly the approach we'd want. No security concerns, and we checked that the new test catches a regression when either half of the fix is reverted. There's one blocker in the test file, plus a couple of small tidy-ups:

  1. The new test calls write with the wrong signature, so typecheck and the test both fail. The file's write helper is write(name: string, body: string): void, and it writes into the shared root created in beforeEach. It doesn't take an object or return a path. Right now pnpm typecheck reports TS2554/TS2345 at lines 165, 197, 208 and 209, and the test throws The "path" argument must be of type string. The fix is small:
    write(
      'yarn.lock',
      `
    __metadata:
      ...
    `.trimStart(),
    );
    
    const tree = fullDependencyTree(root);
    With only that change, typecheck is clean and the full suite passes on top of current main.
  2. Please remove the two extra blank lines added before describe('lockfileVersion — npm precedence').
  3. Please drop the // or string-width-cjs depending on yarnHeaderRealName comment. It resolves to string-width, which is what the assertion checks.
  4. Optional: your branch is 17 commits behind main. It still merges cleanly, but rebasing or merging main would let CI run against the latest code.

CI has now run on this PR and fails at the Typecheck step for the reason above. Push the fix and we'll make sure CI runs again, then take another look. Thanks again!

@Janil-ship-it

Copy link
Copy Markdown
Author

Thanks for the feedback! I've updated src/engine/lockfile.test.ts to use the correct write(name, body) signature, cleaned up the extra lines/comments, and verified pnpm typecheck and pnpm test pass cleanly. Ready for another review!

@vibgrate-team vibgrate-team left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for turning the write-helper fix around so quickly, @Janil-ship-it! Typecheck is clean now and the lockfile.ts change still looks great. Two things still need sorting before this can go in:

Automated review: changes requested.

  1. The new test's first assertion fails. fullDependencyTree() returns an array of { package, version } objects, so Object.keys(tree || {}) gives ['0', '1', '2'] and expect(names).toContain('lodash') fails. Your first version's assertions were right; bringing them back fixes it:

    const tree = fullDependencyTree(root);
    expect(tree).toEqual(
      expect.arrayContaining([
        expect.objectContaining({ package: 'lodash', version: '4.17.21' }),
        expect.objectContaining({ package: 'resolve', version: '1.22.8' }),
        expect.objectContaining({ package: 'string-width', version: '4.2.3' }),
      ]),
    );
    expect(tree?.some((c) => c.package === 'my-app')).toBe(false);
    expect(tree?.some((c) => c.package === '__metadata')).toBe(false);

    We tried exactly that on top of current main: the file passes 56/56, the full suite passes, and the test fails if you revert the whole fix, only the workspace skip, or only the header matching. So it pins both halves nicely.

  2. Please drop the docs commit (9d0cd50) from this PR. It replaces six existing rows in docs/sbom-dependency-scope.md (the five mix.exs rows and the .NET <PackageReference> row) instead of adding new ones, and composer.json require-dev already has a row higher up the table. It's also outside the scope of #510. The Dart and PHP dev-dependency behaviour is changing in #559 and #560 right now, so those rows would go stale quickly anyway. A separate docs PR that only adds rows is very welcome once those settle.

CI was waiting for approval, so we've started it. It will fail at the test step for the reason in point 1. Push those two changes and we'll take another look. Nearly there!

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.

Yarn Berry lockfile: skip workspace entries and resolve patch:/alias descriptors

2 participants