Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make bun understand yarn pnp #924

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7,548 changes: 7,548 additions & 0 deletions packages/knip/fixtures/yarn-pnp/.pnp.cjs

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions packages/knip/fixtures/yarn-pnp/.yarnrc.yml
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made a real yarn workspace to test real pnp api, but it'll be good to see any ideas for this fixture.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
enableGlobalCache: false
3 changes: 3 additions & 0 deletions packages/knip/fixtures/yarn-pnp/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { dep } from '@sample-package/dep'

dep;
3 changes: 3 additions & 0 deletions packages/knip/fixtures/yarn-pnp/knip.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ignore": ["**/.pnp.{cjs,mjs,js}", "**/.pnp.loader.{cjs,mjs,js}", "**/sample-packages/**"]
}
11 changes: 11 additions & 0 deletions packages/knip/fixtures/yarn-pnp/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "@fixtures/yarn-pnp-test",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"packageManager": "[email protected]",
"dependencies": {
"@sample-package/dep": "./sample-packages/dep",
"@sample-package/peer": "./sample-packages/peer"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const dep = 'dep';
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "@sample-package/dep",
"version": "0.0.0",
"main": "./index.js",
"peerDependencies": {
"@sample-package/peer": "*"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const peer = 'peer';
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "@sample-package/peer",
"version": "0.0.0",
"main": "./index.js"
}
31 changes: 31 additions & 0 deletions packages/knip/fixtures/yarn-pnp/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!

__metadata:
version: 8
cacheKey: 10c0

"@fixtures/yarn-pnp-test@workspace:.":
version: 0.0.0-use.local
resolution: "@fixtures/yarn-pnp-test@workspace:."
dependencies:
"@sample-package/dep": ./sample-packages/dep
"@sample-package/peer": ./sample-packages/peer
languageName: unknown
linkType: soft

"@sample-package/dep@file:./sample-packages/dep::locator=%40fixtures%2Fyarn-pnp-test%40workspace%3A.":
version: 0.0.0
resolution: "@sample-package/dep@file:./sample-packages/dep#./sample-packages/dep::hash=537a59&locator=%40fixtures%2Fyarn-pnp-test%40workspace%3A."
peerDependencies:
"@sample-package/peer": "*"
checksum: 10c0/e8bd21d44ae40880d3512bc09edededd1a98716da88727fba4983465d9b452bfebc48257239632ed41d34d75d5fed03183d7422c352e90554e95ebee82c2f83b
languageName: node
linkType: hard

"@sample-package/peer@file:./sample-packages/peer::locator=%40fixtures%2Fyarn-pnp-test%40workspace%3A.":
version: 0.0.0
resolution: "@sample-package/peer@file:./sample-packages/peer#./sample-packages/peer::hash=3bb161&locator=%40fixtures%2Fyarn-pnp-test%40workspace%3A."
checksum: 10c0/ec625a2dd9ddeeccfb18550e0789ddb92c6b65d7e94514f3eb62a222b500297dc2d50aadc748d5e3067374bdf74e634dfa36cec258e334ca5ea19565f85b44e6
languageName: node
linkType: hard
76 changes: 73 additions & 3 deletions packages/knip/src/manifest/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,82 @@
import type { Scripts } from '../types/package-json.js';
import { join } from '../util/path.js';
import { isFile } from '../util/fs.js';
import { dirname, join } from '../util/path.js';
import { _require } from '../util/require.js';

let isPnPEnabled: boolean | 'NOT_DETERMINED_YET' = 'NOT_DETERMINED_YET';

type LoadPackageManifestOptions = { dir: string; packageName: string; cwd: string };

const findNearestPnPFile = (startDir: string): string | null => {
// Find the nearest .pnp.cjs file by traversing up
let currentDir = startDir;
while (currentDir !== '/') {
const pnpPath = join(currentDir, '.pnp.cjs');
if (isFile(pnpPath)) {
return pnpPath;
}
// Move up one directory
const parentDir = dirname(currentDir);
if (parentDir === currentDir) {
break; // Reached root
}
currentDir = parentDir;
}
return null;
};

const tryLoadManifestWithYarnPnp = (cwd: string, packageName: string) => {
if (isPnPEnabled === false) {
return null;
}

try {
if (isPnPEnabled === 'NOT_DETERMINED_YET') {
const pnpPath = findNearestPnPFile(cwd);

if (pnpPath != null) {
const pnp = _require(pnpPath);
pnp.setup();
isPnPEnabled = true;
} else {
isPnPEnabled = false;
}
}

if (isPnPEnabled) {
const pnpApi = _require('pnpapi');

if (pnpApi != null) {
const resolvedPath = pnpApi.resolveRequest(packageName, cwd);

if (resolvedPath) {
const packageLocation = pnpApi.findPackageLocator(resolvedPath);
const packageInformation = pnpApi.getPackageInformation(packageLocation);
const packageJsonPath = join(packageInformation.packageLocation, 'package.json');

// We need to require fs dynamically here because pnp patches it.
const _readFileSync = _require('fs').readFileSync;
const manifest = JSON.parse(_readFileSync(packageJsonPath, 'utf8'));

return manifest;
}
}
}
} catch (_error) {
// Explicitly suppressing errors here
}

return null;
};

export const loadPackageManifest = ({ dir, packageName, cwd }: LoadPackageManifestOptions) => {
// TODO Not sure what's the most efficient way to get a package.json, but this seems to do the job across package
// managers (npm, Yarn, pnpm)
// 1. Try Yarn PnP first
const pnpManifest = tryLoadManifestWithYarnPnp(cwd, packageName);
if (pnpManifest != null) {
return pnpManifest;
}

// 2. Fallback to traditional node_modules resolution
try {
return _require(join(dir, 'node_modules', packageName, 'package.json'));
} catch (_error) {
Expand Down
22 changes: 22 additions & 0 deletions packages/knip/test/yarn-pnp.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { test } from 'bun:test';
import assert from 'node:assert/strict';
import { main } from '../src/index.js';
import { resolve } from '../src/util/path.js';
import baseArguments from './helpers/baseArguments.js';
import baseCounters from './helpers/baseCounters.js';

const cwd = resolve('fixtures/yarn-pnp');

test('Find unused dependencies', async () => {
const { counters } = await main({
...baseArguments,
cwd,
isStrict: true,
});

assert.deepEqual(counters, {

Check failure on line 17 in packages/knip/test/yarn-pnp.test.ts

View workflow job for this annotation

GitHub Actions / macos-latest

AssertionError: Expected values to be strictly deep-equal

: { binaries: 0, classMembers: 0, + dependencies: 1, - dependencies: 0, devDependencies: 0, duplicates: 0, enumMembers: 0, exports: 0, files: 0, at /Users/runner/work/knip/knip/packages/knip/test/yarn-pnp.test.ts:17:10

Check failure on line 17 in packages/knip/test/yarn-pnp.test.ts

View workflow job for this annotation

GitHub Actions / ubuntu-latest

AssertionError: Expected values to be strictly deep-equal

: { binaries: 0, classMembers: 0, + dependencies: 1, - dependencies: 0, devDependencies: 0, duplicates: 0, enumMembers: 0, exports: 0, files: 0, at /home/runner/work/knip/knip/packages/knip/test/yarn-pnp.test.ts:17:10
...baseCounters,
processed: 1,
total: 4,
});
});
Loading