Skip to content
Open
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
67da7b0
refactor: rename pnpm specific type to reuse it for yarn as well
MKruschke Jul 24, 2025
ab690ff
refactor: use extractCatalogDeps for both cases
MKruschke Jul 24, 2025
fcc0159
feat: extract dependencies for yarn catalogs
MKruschke Jul 24, 2025
d436370
feat: updating dependency
MKruschke Jul 25, 2025
8ca30d5
test: added more tests
MKruschke Aug 3, 2025
7a53772
fix: fix typo
MKruschke Aug 3, 2025
d1874cd
Merge branch 'main' into feat/yarn-plugin-catalogs-support
MKruschke Aug 3, 2025
350379e
Merge branch 'main' into feat/yarn-plugin-catalogs-support
MKruschke Aug 3, 2025
fd9bc29
Merge branch 'main' into feat/yarn-plugin-catalogs-support
MKruschke Aug 4, 2025
4cadc5c
Merge branch 'main' into feat/yarn-plugin-catalogs-support
MKruschke Aug 4, 2025
04cb16b
fix: remove debugger stmnt and undo line deletion
MKruschke Aug 5, 2025
3e254ee
Merge remote-tracking branch 'origin/main' into feat/yarn-plugin-cata…
MKruschke Aug 6, 2025
6cc85df
fix: remove core file
MKruschke Aug 6, 2025
5ab19aa
fix: fix lint issues
MKruschke Aug 6, 2025
72b6c13
fix: fix lint issues
MKruschke Aug 6, 2025
0c261e7
fix: fix lint issues
MKruschke Aug 6, 2025
8622e23
test: add more tests
MKruschke Aug 7, 2025
c654c2f
Merge branch 'main' into feat/yarn-plugin-catalogs-support
MKruschke Aug 7, 2025
90437ee
Merge branch 'main' into feat/yarn-plugin-catalogs-support
MKruschke Aug 7, 2025
bfc0732
test: add more tests
MKruschke Aug 7, 2025
6c77671
test: add test comments
MKruschke Aug 7, 2025
72849f0
Merge branch 'main' into feat/yarn-plugin-catalogs-support
MKruschke Aug 7, 2025
3ac804d
test: add more tests
MKruschke Aug 8, 2025
a918957
test: add more tests
MKruschke Aug 8, 2025
414f8c3
Merge branch 'main' into feat/yarn-plugin-catalogs-support
MKruschke Aug 8, 2025
7f20994
fix: remove unused variable
MKruschke Aug 8, 2025
7e38f93
test: add more tests
MKruschke Aug 8, 2025
39dea8d
fix: try to fix istanbul comments
MKruschke Aug 8, 2025
4211ea6
fix: try v8 instead of istanbul
MKruschke Aug 8, 2025
043fd00
Merge branch 'main' into feat/yarn-plugin-catalogs-support
MKruschke Aug 8, 2025
9a75e30
Merge branch 'main' into feat/yarn-plugin-catalogs-support
MKruschke Aug 8, 2025
1e69a45
Merge branch 'main' into feat/yarn-plugin-catalogs-support
viceice Aug 8, 2025
adca60c
Merge remote-tracking branch 'origin/main' into feat/yarn-plugin-cata…
MKruschke Aug 15, 2025
8ac44af
chore: remove debugger statement
MKruschke Aug 15, 2025
0046429
chore: fix v8 ignore statements
MKruschke Aug 15, 2025
eb70ee3
fix: remove core file
MKruschke Aug 15, 2025
02ca083
Merge branch 'main' into feat/yarn-plugin-catalogs-support
MKruschke Aug 25, 2025
ff63e13
fix: applied review suggestions
MKruschke Aug 25, 2025
90ccad6
fix: change to manageFilePattern
MKruschke Sep 14, 2025
693b441
Merge remote-tracking branch 'origin/main' into feat/yarn-plugin-cata…
MKruschke Sep 14, 2025
5f1425f
fix: remove unused import
MKruschke Sep 14, 2025
b95c58f
fix: add return type
MKruschke Sep 15, 2025
216b519
fix: fix tests
MKruschke Sep 15, 2025
91c287d
Merge branch 'main' into feat/yarn-plugin-catalogs-support
MKruschke Sep 15, 2025
3f1aadd
revert: unrelated tests changes
MKruschke Sep 15, 2025
0bfbc90
fix: remove unused code after change the strategy
MKruschke Sep 15, 2025
9ee3c7b
fix: apply review comments
MKruschke Sep 15, 2025
3619d5e
fix: remove imports
MKruschke Sep 15, 2025
bd8a66b
revert: unwanted changes
MKruschke Sep 15, 2025
2a61349
revert: unwanted changes
MKruschke Sep 15, 2025
91bcff6
Merge remote-tracking branch 'origin/main' into feat/yarn-plugin-cata…
MKruschke Sep 19, 2025
6c50ae5
refactore: use loadPackageJson
MKruschke Sep 19, 2025
e66caec
Update lib/modules/manager/npm/extract/common/package-file.spec.ts
MKruschke Sep 19, 2025
8aef009
fix: apply review comments
MKruschke Sep 20, 2025
c681481
fix: remove unused import
MKruschke Sep 20, 2025
7adbb9f
Apply suggestion from @RahulGautamSingh
MKruschke Sep 20, 2025
d73cf56
Apply suggestion from @RahulGautamSingh
MKruschke Sep 20, 2025
bf37bad
fix: apply review comments
MKruschke Sep 20, 2025
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
6 changes: 5 additions & 1 deletion lib/config/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ describe('config/index', () => {
expect(config).toContainEntries([
[
'managerFilePatterns',
['/(^|/)package\\.json$/', '/(^|/)pnpm-workspace\\.yaml$/'],
[
'/(^|/)package\\.json$/',
'/(^|/)pnpm-workspace\\.yaml$/',
'/(^|/)\\.yarnrc\\.yml$/',
],
],
]);
expect(getManagerConfig(parentConfig, 'html')).toContainEntries([
Expand Down
67 changes: 67 additions & 0 deletions lib/modules/manager/npm/extract/common/package-file.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
vi.mock('fs-extra', async () =>
(
await vi.importActual<typeof import('~test/fixtures')>('~test/fixtures')
).fsExtra(),
);

import { GlobalConfig } from '../../../../../config/global';
import { logger } from '../../../../../logger';
import { hasPackageManager } from './package-file';
import { Fixtures } from '~test/fixtures';

describe('modules/manager/npm/extract/common/package-file', () => {
beforeEach(() => {
Fixtures.reset();
GlobalConfig.set({ localDir: '/', cacheDir: '/tmp/cache' });
vitest.clearAllMocks();
});

it('returns true for a valid packageManager with name@range (e.g. [email protected])', async () => {
Fixtures.mock({
'/repo/package.json': JSON.stringify({ packageManager: '[email protected]' }),
});
await expect(hasPackageManager('/repo')).resolves.toBe(true);

expect(logger.trace).toHaveBeenCalledWith(
'npm.hasPackageManager from package.json',
);
});

it('returns true for a valid range like npm@^9', async () => {
Fixtures.mock({
'/repo/package.json': JSON.stringify({ packageManager: 'npm@^9' }),
});
await expect(hasPackageManager('/repo')).resolves.toBe(true);
});

it('returns true for yarn classic pin [email protected]', async () => {
Fixtures.mock({
'/repo/package.json': JSON.stringify({ packageManager: '[email protected]' }),
});
await expect(hasPackageManager('/repo')).resolves.toBe(true);
});

it("returns false when packageManager does not contain '@' (e.g. 'npm')", async () => {
Fixtures.mock({
'/repo/package.json': JSON.stringify({ packageManager: 'npm' }),
});
await expect(hasPackageManager('/repo')).resolves.toBe(false);
});

it('returns false when packageManager is missing', async () => {
Fixtures.mock({ '/repo/package.json': JSON.stringify({ name: 'demo' }) });
await expect(hasPackageManager('/repo')).resolves.toBe(false);
});

it('returns false when package.json is invalid', async () => {
Fixtures.mock({ '/repo/package.json': '{ not: valid json' });
await expect(hasPackageManager('/repo')).resolves.toBe(false);
});

it('returns false if packageManager is an empty string', async () => {
Fixtures.mock({
'/repo/package.json': JSON.stringify({ packageManager: '' }),
});
await expect(hasPackageManager('/repo')).resolves.toBe(false);
});
});
14 changes: 14 additions & 0 deletions lib/modules/manager/npm/extract/common/package-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { logger } from '../../../../../logger';
import { regEx } from '../../../../../util/regex';
import type { PackageDependency, PackageFileContent } from '../../../types';
import type { NpmManagerData } from '../../types';
import { loadPackageJson } from '../../utils';
import type { NpmPackage, NpmPackageDependency } from '../types';
import {
extractDependency,
Expand Down Expand Up @@ -147,3 +148,16 @@ export function extractPackageJson(
},
};
}

export async function hasPackageManager(
packageJsonDir: string,
): Promise<boolean> {
logger.trace(`npm.hasPackageManager from package.json`);

const packageJsonResult = await loadPackageJson(packageJsonDir);

return (
is.nonEmptyString(packageJsonResult?.packageManager?.name) &&
is.nonEmptyString(packageJsonResult?.packageManager?.version)
);
}
143 changes: 142 additions & 1 deletion lib/modules/manager/npm/extract/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { codeBlock } from 'common-tags';
import { extractAllPackageFiles } from '..';
import { logger } from '../../../../logger';
import * as fsUtil from '../../../../util/fs/util';
import type { ExtractConfig } from '../../types';
import { postExtract } from './post';
import * as npmExtract from '.';
Expand All @@ -15,6 +16,10 @@ const defaultExtractConfig = {

const input01Content = Fixtures.get('inputs/01.json', '..');
const input02Content = Fixtures.get('inputs/02.json', '..');
const input01PackageManager = Fixtures.get(
'inputs/01-package-manager.json',
'..',
);
const input01GlobContent = Fixtures.get('inputs/01-glob.json', '..');
const workspacesContent = Fixtures.get('inputs/workspaces.json', '..');
const vendorisedContent = Fixtures.get('is-object.json', '..');
Expand Down Expand Up @@ -927,7 +932,7 @@ describe('modules/manager/npm/extract/index', () => {
const pJsonStr = JSON.stringify(pJson);
const res = await npmExtract.extractPackageFile(
pJsonStr,
'package.json',
'.yarnrc.yml',
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good spot I changed one test to much after the refactoring. Will revert it

defaultExtractConfig,
);
expect(res).toMatchObject({
Expand Down Expand Up @@ -1268,6 +1273,142 @@ describe('modules/manager/npm/extract/index', () => {
},
]);
});

it('extracts yarnrc.yml and adds it as packageFile', async () => {
const yarnrc = codeBlock`
nodeLinker: node-modules

plugins:
- checksum: 4cb9601cfc0c71e5b0ffd0a85b78e37430b62257040714c2558298ce1fc058f4e918903f0d1747a4fef3f58e15722c35bd76d27492d9d08aa5b04e235bf43b22
path: .yarn/plugins/@yarnpkg/plugin-catalogs.cjs
spec: 'https://raw.githubusercontent.com/toss/yarn-plugin-catalogs/main/bundles/%40yarnpkg/plugin-catalogs.js'

catalogs:
list:
is-positive: 1.0.0
`;
fs.readLocalFile.mockResolvedValueOnce(yarnrc);

fs.getParentDir.mockReturnValue('./');

vi.spyOn(fsUtil, 'ensureLocalPath').mockImplementation(
(p) => './.yarnrc.yml',
);

fs.readLocalFile.mockResolvedValueOnce(input02Content);

const res = await extractAllPackageFiles(defaultExtractConfig, [
'.yarnrc.yml',
]);

expect(res).toEqual([
{
deps: [
{
currentValue: '1.0.0',
datasource: 'npm',
depName: 'is-positive',
depType: 'yarn.catalog.default',
prettyDepType: 'yarn.catalog.default',
},
],
managerData: {
hasPackageManager: false,
},
packageFile: '.yarnrc.yml',
},
]);
});

it('extracts yarnrc.yml and adds it as packageFile and packageManager to true', async () => {
const yarnrc = codeBlock`
nodeLinker: node-modules

plugins:
- checksum: 4cb9601cfc0c71e5b0ffd0a85b78e37430b62257040714c2558298ce1fc058f4e918903f0d1747a4fef3f58e15722c35bd76d27492d9d08aa5b04e235bf43b22
path: .yarn/plugins/@yarnpkg/plugin-catalogs.cjs
spec: 'https://raw.githubusercontent.com/toss/yarn-plugin-catalogs/main/bundles/%40yarnpkg/plugin-catalogs.js'

catalogs:
list:
is-positive: 1.0.0
`;
fs.getParentDir.mockReturnValue('./');

vi.spyOn(fsUtil, 'ensureLocalPath').mockImplementation(
(p) => './.yarnrc.yml',
);

fs.readLocalFile.mockResolvedValueOnce(yarnrc);
fs.readLocalFile.mockResolvedValueOnce(input01PackageManager);

const res = await extractAllPackageFiles(defaultExtractConfig, [
'.yarnrc.yml',
]);

expect(res[0]).toEqual({
deps: [
{
currentValue: '1.0.0',
datasource: 'npm',
depName: 'is-positive',
depType: 'yarn.catalog.default',
prettyDepType: 'yarn.catalog.default',
},
],
managerData: {
hasPackageManager: true,
},
packageFile: '.yarnrc.yml',
});
});

it('extracts yarnrc.yml and adds it as packageFile and packageManager to false if no deps', async () => {
const yarnrc = codeBlock`
nodeLinker: node-modules

plugins:
- checksum: 4cb9601cfc0c71e5b0ffd0a85b78e37430b62257040714c2558298ce1fc058f4e918903f0d1747a4fef3f58e15722c35bd76d27492d9d08aa5b04e235bf43b22
path: .yarn/plugins/@yarnpkg/plugin-catalogs.cjs
spec: 'https://raw.githubusercontent.com/toss/yarn-plugin-catalogs/main/bundles/%40yarnpkg/plugin-catalogs.js'

catalogs:
list:
is-positive: 1.0.0
`;

fs.getParentDir.mockReturnValue('./');

vi.spyOn(fsUtil, 'ensureLocalPath').mockImplementation(
(p) => './.yarnrc.yml',
);

fs.readLocalFile.mockResolvedValueOnce(yarnrc);

fs.readLocalFile.mockResolvedValueOnce(
'{"name": "simulate deps to be null", brokenJsonHere: }',
);

const res = await extractAllPackageFiles(defaultExtractConfig, [
'.yarnrc.yml',
]);

expect(res[0]).toEqual({
deps: [
{
currentValue: '1.0.0',
datasource: 'npm',
depName: 'is-positive',
depType: 'yarn.catalog.default',
prettyDepType: 'yarn.catalog.default',
},
],
managerData: {
hasPackageManager: false,
},
packageFile: '.yarnrc.yml',
});
});
});

describe('.postExtract()', () => {
Expand Down
44 changes: 35 additions & 9 deletions lib/modules/manager/npm/extract/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { GlobalConfig } from '../../../../config/global';
import { logger } from '../../../../logger';
import {
findLocalSiblingOrParent,
getParentDir,
getSiblingFileName,
readLocalFile,
} from '../../../../util/fs';
import { ensureLocalPath } from '../../../../util/fs/util';
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
import { ensureLocalPath } from '../../../../util/fs/util';

wrong import path anyways

import { newlineRegex, regEx } from '../../../../util/regex';
import { NpmDatasource } from '../../../datasource/npm';

Expand All @@ -17,11 +19,11 @@ import type {
import type { YarnConfig } from '../schema';
import type { NpmLockFiles, NpmManagerData } from '../types';
import { getExtractedConstraints } from './common/dependency';
import { extractPackageJson } from './common/package-file';
import { extractPackageJson, hasPackageManager } from './common/package-file';
import { extractPnpmWorkspaceFile, tryParsePnpmWorkspaceYaml } from './pnpm';
import { postExtract } from './post';
import type { NpmPackage } from './types';
import { isZeroInstall } from './yarn';
import { extractYarnCatalogs, isZeroInstall } from './yarn';
import {
loadConfigFromLegacyYarnrc,
loadConfigFromYarnrcYml,
Expand Down Expand Up @@ -254,13 +256,37 @@ export async function extractAllPackageFiles(
});
}
} else {
logger.trace({ packageFile }, `Extracting as a package.json file`);
const deps = await extractPackageFile(content, packageFile, config);
if (deps) {
npmFiles.push({
...deps,
packageFile,
});
if (packageFile.endsWith('json')) {
logger.trace({ packageFile }, `Extracting as a package.json file`);

const deps = await extractPackageFile(content, packageFile, config);
if (deps) {
npmFiles.push({
...deps,
packageFile,
});
}
} else {
logger.trace({ packageFile }, `Extracting as a .yarnrc.yml file`);

const yarnConfig = loadConfigFromYarnrcYml(content);

if (yarnConfig?.catalogs) {
const hasPackageManagerResult = await hasPackageManager(
getParentDir(ensureLocalPath(packageFile)),
);
const catalogsDeps = await extractYarnCatalogs(
yarnConfig.catalogs,
packageFile,
hasPackageManagerResult,
);
if (catalogsDeps) {
npmFiles.push({
...catalogsDeps,
packageFile,
});
}
}
}
}
} else {
Expand Down
Loading