Skip to content

Commit

Permalink
refactor: package-file
Browse files Browse the repository at this point in the history
  • Loading branch information
rarkins committed Sep 18, 2023
1 parent ecf903c commit b66189b
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 96 deletions.
116 changes: 116 additions & 0 deletions lib/modules/manager/npm/extract/common/package-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import is from '@sindresorhus/is';
import { CONFIG_VALIDATION } from '../../../../../constants/error-messages';
import { logger } from '../../../../../logger';
import { regEx } from '../../../../../util/regex';
import type { PackageDependency, PackageFileContent } from '../../../types';
import type { NpmManagerData } from '../../types';
import type { NpmPackage, NpmPackageDependency } from '../types';
import {
extractDependency,
getExtractedConstraints,
parseDepName,
} from './dependency';
import { setNodeCommitTopic } from './node';
import { extractOverrideDepsRec } from './overrides';

export function extractPackageJson(
packageJson: NpmPackage,
packageFile: string
): PackageFileContent<NpmManagerData> | null {
logger.trace(`npm.extractPackageJson(${packageFile})`);
const deps: PackageDependency[] = [];

if (packageJson._id && packageJson._args && packageJson._from) {
logger.debug({ packageFile }, 'Ignoring vendorised package.json');
return null;
}
if (packageFile !== 'package.json' && packageJson.renovate) {
const error = new Error(CONFIG_VALIDATION);
error.validationSource = packageFile;
error.validationError =
'Nested package.json must not contain Renovate configuration. Please use `packageRules` with `matchFileNames` in your main config instead.';
throw error;
}
const packageJsonName = packageJson.name;
logger.debug(
`npm file ${packageFile} has name ${JSON.stringify(packageJsonName)}`
);
const packageFileVersion = packageJson.version;

const depTypes = {
dependencies: 'dependency',
devDependencies: 'devDependency',
optionalDependencies: 'optionalDependency',
peerDependencies: 'peerDependency',
engines: 'engine',
volta: 'volta',
resolutions: 'resolutions',
packageManager: 'packageManager',
overrides: 'overrides',
};

for (const depType of Object.keys(depTypes) as (keyof typeof depTypes)[]) {
let dependencies = packageJson[depType];
if (dependencies) {
try {
if (depType === 'packageManager') {
const match = regEx('^(?<name>.+)@(?<range>.+)$').exec(
dependencies as string
);
// istanbul ignore next
if (!match?.groups) {
break;
}
dependencies = { [match.groups.name]: match.groups.range };
}
for (const [key, val] of Object.entries(
dependencies as NpmPackageDependency
)) {
const depName = parseDepName(depType, key);
let dep: PackageDependency = {
depType,
depName,
};
if (depName !== key) {
dep.managerData = { key };
}
if (depType === 'overrides' && !is.string(val)) {
// TODO: fix type #22198
deps.push(
...extractOverrideDepsRec(
[depName],
val as unknown as NpmManagerData
)
);
} else {
// TODO: fix type #22198
dep = { ...dep, ...extractDependency(depType, depName, val!) };
setNodeCommitTopic(dep);
dep.prettyDepType = depTypes[depType];
deps.push(dep);
}
}
} catch (err) /* istanbul ignore next */ {
logger.debug(
{ fileName: packageFile, depType, err },
'Error parsing package.json'
);
return null;
}
}
}

const extractedConstraints = getExtractedConstraints(deps);

return {
deps,
extractedConstraints,
packageFileVersion,
managerData: {
packageJsonName,
hasPackageManager: is.nonEmptyStringAndNotWhitespace(
packageJson.packageManager
),
},
};
}
110 changes: 14 additions & 96 deletions lib/modules/manager/npm/extract/index.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
import is from '@sindresorhus/is';
import { GlobalConfig } from '../../../../config/global';
import { CONFIG_VALIDATION } from '../../../../constants/error-messages';
import { logger } from '../../../../logger';
import { getSiblingFileName, readLocalFile } from '../../../../util/fs';
import { newlineRegex, regEx } from '../../../../util/regex';

import type {
ExtractConfig,
PackageDependency,
PackageFile,
PackageFileContent,
} from '../../types';
import type { NpmLockFiles, NpmManagerData } from '../types';
import {
extractDependency,
getExtractedConstraints,
parseDepName,
} from './common/dependency';
import { setNodeCommitTopic } from './common/node';
import { extractOverrideDepsRec } from './common/overrides';
import { getExtractedConstraints } from './common/dependency';
import { extractPackageJson } from './common/package-file';
import { postExtract } from './post';
import type { NpmPackage, NpmPackageDependency } from './types';
import type { NpmPackage } from './types';
import { isZeroInstall } from './yarn';
import {
YarnConfig,
Expand All @@ -40,7 +33,6 @@ export async function extractPackageFile(
): Promise<PackageFileContent<NpmManagerData> | null> {
logger.trace(`npm.extractPackageFile(${packageFile})`);
logger.trace({ content });
const deps: PackageDependency[] = [];
let packageJson: NpmPackage;
try {
packageJson = JSON.parse(content);
Expand All @@ -49,22 +41,11 @@ export async function extractPackageFile(
return null;
}

if (packageJson._id && packageJson._args && packageJson._from) {
logger.debug({ packageFile }, 'Ignoring vendorised package.json');
const res = extractPackageJson(packageJson, packageFile);
if (!res) {
return null;
}
if (packageFile !== 'package.json' && packageJson.renovate) {
const error = new Error(CONFIG_VALIDATION);
error.validationSource = packageFile;
error.validationError =
'Nested package.json must not contain Renovate configuration. Please use `packageRules` with `matchFileNames` in your main config instead.';
throw error;
}
const packageJsonName = packageJson.name;
logger.debug(
`npm file ${packageFile} has name ${JSON.stringify(packageJsonName)}`
);
const packageFileVersion = packageJson.version;

let workspacesPackages: string[] | undefined;
if (is.array(packageJson.workspaces)) {
workspacesPackages = packageJson.workspaces;
Expand Down Expand Up @@ -180,74 +161,12 @@ export async function extractPackageFile(
lernaJsonFile = undefined;
}

const depTypes = {
dependencies: 'dependency',
devDependencies: 'devDependency',
optionalDependencies: 'optionalDependency',
peerDependencies: 'peerDependency',
engines: 'engine',
volta: 'volta',
resolutions: 'resolutions',
packageManager: 'packageManager',
overrides: 'overrides',
};

for (const depType of Object.keys(depTypes) as (keyof typeof depTypes)[]) {
let dependencies = packageJson[depType];
if (dependencies) {
try {
if (depType === 'packageManager') {
const match = regEx('^(?<name>.+)@(?<range>.+)$').exec(
dependencies as string
);
// istanbul ignore next
if (!match?.groups) {
break;
}
dependencies = { [match.groups.name]: match.groups.range };
}
for (const [key, val] of Object.entries(
dependencies as NpmPackageDependency
)) {
const depName = parseDepName(depType, key);
let dep: PackageDependency = {
depType,
depName,
};
if (depName !== key) {
dep.managerData = { key };
}
if (depType === 'overrides' && !is.string(val)) {
// TODO: fix type #22198
deps.push(
...extractOverrideDepsRec(
[depName],
val as unknown as NpmManagerData
)
);
} else {
// TODO: fix type #22198
dep = { ...dep, ...extractDependency(depType, depName, val!) };
setNodeCommitTopic(dep);
dep.prettyDepType = depTypes[depType];
deps.push(dep);
}
}
} catch (err) /* istanbul ignore next */ {
logger.debug(
{ fileName: packageFile, depType, err },
'Error parsing package.json'
);
return null;
}
}
}
if (deps.length === 0) {
if (res.deps.length === 0) {
logger.debug('Package file has no deps');
if (
!(
!!packageJsonName ||
!!packageFileVersion ||
!!res.managerData?.packageJsonName ||
!!res.packageFileVersion ||
!!npmrc ||
!!lernaJsonFile ||
workspacesPackages
Expand All @@ -259,7 +178,7 @@ export async function extractPackageFile(
}
let skipInstalls = config.skipInstalls;
if (skipInstalls === null) {
const hasFancyRefs = !!deps.some(
const hasFancyRefs = !!res.deps.some(
(dep) =>
!!dep.currentValue?.startsWith('file:') ||
!!dep.currentValue?.startsWith('npm:')
Expand All @@ -277,10 +196,10 @@ export async function extractPackageFile(
}
}

const extractedConstraints = getExtractedConstraints(deps);
const extractedConstraints = getExtractedConstraints(res.deps);

if (yarnConfig) {
for (const dep of deps) {
for (const dep of res.deps) {
if (dep.depName) {
const registryUrlFromYarnConfig = resolveRegistryUrl(
dep.depName,
Expand All @@ -294,15 +213,14 @@ export async function extractPackageFile(
}

return {
deps,
packageFileVersion,
...res,
npmrc,
managerData: {
...res.managerData,
...lockFiles,
lernaClient,
lernaJsonFile,
lernaPackages,
packageJsonName,
yarnZeroInstall,
hasPackageManager: is.nonEmptyStringAndNotWhitespace(
packageJson.packageManager
Expand Down

0 comments on commit b66189b

Please sign in to comment.