mirror of
https://github.com/renovatebot/renovate.git
synced 2026-11-01 02:00:10 +00:00
9d74625d93
* build(deps): update dependency prettier to v3.9.6 | datasource | package | from | to | | ---------- | -------- | ----- | ----- | | npm | prettier | 3.8.1 | 3.9.6 | * chore: prettier fixes --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Michael Kriese <michael.kriese@mend.io>
314 lines
9.4 KiB
TypeScript
314 lines
9.4 KiB
TypeScript
import {
|
|
isArray,
|
|
isNonEmptyObject,
|
|
isNonEmptyStringAndNotWhitespace,
|
|
isString,
|
|
} from '@sindresorhus/is';
|
|
import upath from 'upath';
|
|
import { logger } from '../../../../logger/index.ts';
|
|
import {
|
|
findLocalSiblingOrParent,
|
|
getSiblingFileName,
|
|
readLocalFile,
|
|
} from '../../../../util/fs/index.ts';
|
|
import { NpmDatasource } from '../../../datasource/npm/index.ts';
|
|
|
|
import type {
|
|
ExtractConfig,
|
|
PackageFile,
|
|
PackageFileContent,
|
|
} from '../../types.ts';
|
|
import { resolveNpmrc } from '../npmrc.ts';
|
|
import { PnpmWorkspaceFile, type YarnConfig } from '../schema.ts';
|
|
import type { NpmLockFiles, NpmManagerData } from '../types.ts';
|
|
import { getExtractedConstraints } from './common/dependency.ts';
|
|
import {
|
|
extractPackageJson,
|
|
hasPackageManager,
|
|
} from './common/package-file.ts';
|
|
import {
|
|
applyPnpmWorkspaceRegistries,
|
|
extractPnpmWorkspaceFile,
|
|
} from './pnpm.ts';
|
|
import { postExtract } from './post/index.ts';
|
|
import type { NpmPackage } from './types.ts';
|
|
import { extractYarnCatalogs, isZeroInstall } from './yarn.ts';
|
|
import {
|
|
loadConfigFromLegacyYarnrc,
|
|
loadConfigFromYarnrcYml,
|
|
resolveRegistryUrl,
|
|
} from './yarnrc.ts';
|
|
|
|
function hasMultipleLockFiles(lockFiles: NpmLockFiles): boolean {
|
|
return Object.values(lockFiles).filter(isString).length > 1;
|
|
}
|
|
|
|
export async function extractPackageFile(
|
|
content: string,
|
|
packageFile: string,
|
|
config: ExtractConfig,
|
|
): Promise<PackageFileContent<NpmManagerData> | null> {
|
|
logger.trace(`npm.extractPackageFile(${packageFile})`);
|
|
logger.trace({ content });
|
|
let packageJson: NpmPackage;
|
|
try {
|
|
packageJson = JSON.parse(content);
|
|
} catch {
|
|
logger.debug({ packageFile }, `Invalid JSON`);
|
|
return null;
|
|
}
|
|
|
|
const res = extractPackageJson(packageJson, packageFile);
|
|
if (!res) {
|
|
return null;
|
|
}
|
|
|
|
let workspacesPackages: string[] | undefined;
|
|
if (isArray(packageJson.workspaces)) {
|
|
workspacesPackages = packageJson.workspaces;
|
|
} else {
|
|
workspacesPackages = packageJson.workspaces?.packages;
|
|
}
|
|
|
|
const lockFiles: NpmLockFiles = {
|
|
yarnLock: 'yarn.lock',
|
|
packageLock: 'package-lock.json',
|
|
shrinkwrapJson: 'npm-shrinkwrap.json',
|
|
pnpmLockFile: 'pnpm-lock.yaml',
|
|
};
|
|
|
|
for (const [key, val] of Object.entries(lockFiles) as [
|
|
'yarnLock' | 'packageLock' | 'shrinkwrapJson' | 'pnpmLockFile',
|
|
string,
|
|
][]) {
|
|
const filePath = getSiblingFileName(packageFile, val);
|
|
if (await readLocalFile(filePath, 'utf8')) {
|
|
lockFiles[key] = filePath;
|
|
} else {
|
|
lockFiles[key] = undefined;
|
|
}
|
|
}
|
|
lockFiles.npmLock = lockFiles.packageLock ?? lockFiles.shrinkwrapJson;
|
|
delete lockFiles.packageLock;
|
|
delete lockFiles.shrinkwrapJson;
|
|
|
|
if (hasMultipleLockFiles(lockFiles)) {
|
|
logger.warn(
|
|
'Updating multiple npm lock files is deprecated and support will be removed in future versions.',
|
|
);
|
|
}
|
|
|
|
const { npmrc, npmrcFileName } = await resolveNpmrc(packageFile, config);
|
|
|
|
const yarnrcYmlFileName = await findLocalSiblingOrParent(
|
|
packageFile,
|
|
'.yarnrc.yml',
|
|
);
|
|
const yarnZeroInstall = yarnrcYmlFileName
|
|
? await isZeroInstall(yarnrcYmlFileName)
|
|
: false;
|
|
|
|
let yarnrcConfig: YarnConfig | null = null;
|
|
const repoYarnrcYml = yarnrcYmlFileName
|
|
? await readLocalFile(yarnrcYmlFileName, 'utf8')
|
|
: null;
|
|
if (isString(repoYarnrcYml) && repoYarnrcYml.trim().length > 0) {
|
|
yarnrcConfig = loadConfigFromYarnrcYml(repoYarnrcYml);
|
|
}
|
|
|
|
const legacyYarnrcFileName = await findLocalSiblingOrParent(
|
|
packageFile,
|
|
'.yarnrc',
|
|
);
|
|
const repoLegacyYarnrc = legacyYarnrcFileName
|
|
? await readLocalFile(legacyYarnrcFileName, 'utf8')
|
|
: null;
|
|
if (isString(repoLegacyYarnrc) && repoLegacyYarnrc.trim().length > 0) {
|
|
yarnrcConfig = loadConfigFromLegacyYarnrc(repoLegacyYarnrc);
|
|
}
|
|
|
|
let pnpmWorkspaceRegistry: string | undefined;
|
|
let pnpmWorkspaceRegistries: Record<string, string> | undefined;
|
|
const pnpmWorkspaceYamlFileName = await findLocalSiblingOrParent(
|
|
packageFile,
|
|
'pnpm-workspace.yaml',
|
|
);
|
|
const repoPnpmWorkspaceYaml = pnpmWorkspaceYamlFileName
|
|
? await readLocalFile(pnpmWorkspaceYamlFileName, 'utf8')
|
|
: null;
|
|
if (isNonEmptyStringAndNotWhitespace(repoPnpmWorkspaceYaml)) {
|
|
const parsed = await PnpmWorkspaceFile.safeParseAsync(
|
|
repoPnpmWorkspaceYaml,
|
|
);
|
|
if (parsed.success) {
|
|
pnpmWorkspaceRegistry = parsed.data.registry;
|
|
pnpmWorkspaceRegistries = parsed.data.registries;
|
|
} else {
|
|
logger.debug(
|
|
{ packageFile: pnpmWorkspaceYamlFileName, err: parsed.error },
|
|
'Failed to parse pnpm-workspace.yaml',
|
|
);
|
|
}
|
|
}
|
|
|
|
if (res.deps.length === 0) {
|
|
logger.debug('Package file has no deps');
|
|
if (!(
|
|
!!res.managerData?.packageJsonName ||
|
|
!!res.packageFileVersion ||
|
|
!!npmrc ||
|
|
workspacesPackages
|
|
)) {
|
|
logger.debug('Skipping file');
|
|
return null;
|
|
}
|
|
}
|
|
let skipInstalls = config.skipInstalls;
|
|
if (skipInstalls === null) {
|
|
const hasFancyRefs = !!res.deps.some(
|
|
(dep) =>
|
|
!!dep.currentValue?.startsWith('file:') ||
|
|
!!dep.currentValue?.startsWith('npm:'),
|
|
);
|
|
if ((hasFancyRefs && !!lockFiles.npmLock) || yarnZeroInstall) {
|
|
// https://github.com/npm/cli/issues/1432
|
|
// Explanation:
|
|
// - npm install --package-lock-only is buggy for transitive deps in file: and npm: references
|
|
// - So we set skipInstalls to false if file: or npm: refs are found *and* the user hasn't explicitly set the value already
|
|
// - Also, do not skip install if Yarn zero-install is used
|
|
logger.debug('Automatically setting skipInstalls to false');
|
|
skipInstalls = false;
|
|
} else {
|
|
skipInstalls = true;
|
|
}
|
|
}
|
|
|
|
const extractedConstraints = getExtractedConstraints(res.deps);
|
|
|
|
if (yarnrcConfig) {
|
|
for (const dep of res.deps) {
|
|
// v8 ignore else -- TODO: add tests #40625
|
|
if (dep.depName) {
|
|
const registryUrlFromYarnrcConfig = resolveRegistryUrl(
|
|
dep.packageName ?? dep.depName,
|
|
yarnrcConfig,
|
|
);
|
|
if (
|
|
registryUrlFromYarnrcConfig &&
|
|
dep.datasource === NpmDatasource.id
|
|
) {
|
|
dep.registryUrls = [registryUrlFromYarnrcConfig];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Applied after the yarnrc resolution so pnpm-workspace.yaml wins in pnpm repos
|
|
applyPnpmWorkspaceRegistries(
|
|
res.deps,
|
|
pnpmWorkspaceRegistries,
|
|
pnpmWorkspaceRegistry,
|
|
);
|
|
|
|
return {
|
|
...res,
|
|
npmrc,
|
|
managerData: {
|
|
...res.managerData,
|
|
...lockFiles,
|
|
yarnZeroInstall,
|
|
hasPackageManager:
|
|
isNonEmptyStringAndNotWhitespace(packageJson.packageManager) ||
|
|
isNonEmptyObject(packageJson.devEngines?.packageManager),
|
|
workspacesPackages,
|
|
npmrcFileName, // store npmrc file name so we can later tell if it came from the workspace or not
|
|
},
|
|
skipInstalls,
|
|
extractedConstraints,
|
|
};
|
|
}
|
|
|
|
export async function extractAllPackageFiles(
|
|
config: ExtractConfig,
|
|
packageFiles: string[],
|
|
): Promise<PackageFile<NpmManagerData>[]> {
|
|
const npmFiles: PackageFile<NpmManagerData>[] = [];
|
|
for (const packageFile of packageFiles) {
|
|
const content = await readLocalFile(packageFile, 'utf8');
|
|
if (content) {
|
|
// pnpm workspace files are their own package file, defined via managerFilePatterns.
|
|
if (packageFile.endsWith('pnpm-workspace.yaml')) {
|
|
logger.trace(
|
|
{ packageFile },
|
|
`Extracting as a pnpm-workspace.yaml file`,
|
|
);
|
|
const parsedPnpmWorkspaceYaml =
|
|
await PnpmWorkspaceFile.safeParseAsync(content);
|
|
if (parsedPnpmWorkspaceYaml.success) {
|
|
logger.trace(
|
|
{ packageFile },
|
|
`Extracting file as a pnpm workspace YAML file`,
|
|
);
|
|
const deps = await extractPnpmWorkspaceFile(
|
|
parsedPnpmWorkspaceYaml.data,
|
|
packageFile,
|
|
);
|
|
// v8 ignore else -- TODO: add test #40625
|
|
if (deps) {
|
|
npmFiles.push({
|
|
...deps,
|
|
packageFile,
|
|
});
|
|
}
|
|
} else {
|
|
logger.warn(
|
|
{ packageFile, err: parsedPnpmWorkspaceYaml.error },
|
|
`Failed to parse pnpm-workspace.yaml file`,
|
|
);
|
|
}
|
|
} else {
|
|
if (packageFile.endsWith('json')) {
|
|
logger.trace({ packageFile }, `Extracting as a package.json file`);
|
|
|
|
const deps = await extractPackageFile(content, packageFile, config);
|
|
// v8 ignore else -- TODO: add tests #40625
|
|
if (deps) {
|
|
npmFiles.push({
|
|
...deps,
|
|
packageFile,
|
|
});
|
|
}
|
|
} else {
|
|
logger.trace({ packageFile }, `Extracting as a .yarnrc.yml file`);
|
|
|
|
const yarnConfig = loadConfigFromYarnrcYml(content);
|
|
|
|
// v8 ignore else -- TODO: add tests #40625
|
|
if (yarnConfig?.catalogs || yarnConfig?.catalog) {
|
|
const hasPackageManagerResult = await hasPackageManager(
|
|
upath.dirname(packageFile),
|
|
);
|
|
const catalogsDeps = await extractYarnCatalogs(
|
|
{ catalog: yarnConfig.catalog, catalogs: yarnConfig.catalogs },
|
|
packageFile,
|
|
hasPackageManagerResult,
|
|
);
|
|
// v8 ignore else -- TODO: add tests #40625
|
|
if (catalogsDeps) {
|
|
npmFiles.push({
|
|
...catalogsDeps,
|
|
packageFile,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
logger.debug({ packageFile }, `No content found`);
|
|
}
|
|
}
|
|
|
|
await postExtract(npmFiles);
|
|
return npmFiles;
|
|
}
|