eslint-plugin-sonarjs
Version:
168 lines (167 loc) • 6.76 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.packageJsonManifestResolver = void 0;
const files_js_1 = require("../../files.js");
const index_js_1 = require("../index.js");
const closest_js_1 = require("../../find-up/closest.js");
const helpers_js_1 = require("./helpers.js");
const parse_js_1 = require("../parse.js");
const parsed_dependency_files_js_1 = require("../parsed-dependency-files.js");
exports.packageJsonManifestResolver = {
resolve(dir, topDir, fileSystem) {
const packageJson = (0, helpers_js_1.getManifestFileInDir)(index_js_1.PACKAGE_JSON, dir, topDir, fileSystem);
if (!packageJson) {
return [];
}
let parsedPackageJson = (0, parsed_dependency_files_js_1.parsePackageJson)(packageJson) ?? {};
let catalogSource = undefined;
const closestParent = findClosestParentPackageJsonWithCatalogs(dir, topDir, fileSystem);
if (closestParent) {
// If the closest parent package.json has catalogs defined, we use it as the catalog source for resolving catalog references.
const workspaces = Array.isArray(closestParent.workspaces)
? undefined
: closestParent.workspaces;
catalogSource = {
catalog: workspaces?.catalog ?? closestParent.catalog,
catalogs: workspaces?.catalogs ?? closestParent.catalogs,
};
}
else {
// No parent package.json with catalogs found, we check if there's a pnpm workspace file that we can use as a catalog source.
const pnpmWorkspaceFile = closest_js_1.closestPatternCache
.get(index_js_1.PNPM_WORKSPACE_YAML, fileSystem)
.get(topDir)
.get(dir);
const parsedPnpmWorkspace = pnpmWorkspaceFile
? (0, parsed_dependency_files_js_1.parsePnpmWorkspace)(pnpmWorkspaceFile)
: undefined;
if (parsedPnpmWorkspace) {
parsedPackageJson = injectWorkspacePackages(parsedPackageJson, parsedPnpmWorkspace);
catalogSource = parsedPnpmWorkspace;
}
}
parsedPackageJson = resolveCatalogReferences(parsedPackageJson, catalogSource);
// Match Node's resolution: explicit "module" is ESM, anything else (including
// missing "type") is CommonJS.
const moduleType = parsedPackageJson.type === 'module' ? 'module' : 'commonjs';
return [
{
type: 'package-json',
manifest: parsedPackageJson,
dependencies: buildDependencies(parsedPackageJson),
moduleType,
},
];
},
};
function buildDependencies(packageJson) {
const dependencies = new Map();
const fieldsToVisit = [
'name',
'dependencies',
'devDependencies',
'peerDependencies',
'optionalDependencies',
'_moduleAliases',
'workspaces',
];
for (const field of fieldsToVisit) {
if (!packageJson[field]) {
continue;
}
if (field === 'name') {
(0, parse_js_1.addDependencies)(dependencies, { [packageJson[field]]: '*' });
continue;
}
if (field === 'workspaces') {
(0, parse_js_1.addDependenciesArray)(dependencies, Array.isArray(packageJson[field])
? packageJson[field]
: (packageJson[field]?.packages ?? []));
continue;
}
(0, parse_js_1.addDependencies)(dependencies, packageJson[field]);
}
return dependencies;
}
function resolveCatalogReferences(packageJson, catalogSource) {
const depFields = [
'dependencies',
'devDependencies',
'peerDependencies',
'optionalDependencies',
];
const modifiedPackageJson = { ...packageJson };
for (const field of depFields) {
const deps = packageJson[field];
if (!deps) {
continue;
}
const resolvedDeps = {};
for (const [depName, depVersion] of Object.entries(deps)) {
if (typeof depVersion === 'string' && depVersion.startsWith('catalog:')) {
const catalogName = depVersion.slice('catalog:'.length).trim() || 'default';
const resolvedDep = catalogName === 'default'
? catalogSource?.catalog?.[depName]
: catalogSource?.catalogs?.[catalogName]?.[depName];
resolvedDeps[depName] = resolvedDep ?? depVersion;
!resolvedDep &&
console.debug(`Dependency "${depName}" could not be resolved for catalog "${catalogName}"`);
}
else {
resolvedDeps[depName] = depVersion ?? '';
}
}
modifiedPackageJson[field] = resolvedDeps;
}
return modifiedPackageJson;
}
function injectWorkspacePackages(packageJson, pnpmWorkspace) {
if (!pnpmWorkspace.packages || packageJson.workspaces) {
return packageJson;
}
return {
...packageJson,
workspaces: pnpmWorkspace.packages,
};
}
/**
* Find the closest parent package.json with catalogs defined.
* @param dir Directory to start the search from
* @param topDir Top directory to stop the search at
* @param fileSystem Filesystem to use for the search
* @returns The closest parent package.json with catalogs, or undefined if no package.json with catalogs is found
*/
function findClosestParentPackageJsonWithCatalogs(dir, topDir, fileSystem) {
if (dir === topDir) {
// No point in searching for parent package.json if we're already at the top directory.
return undefined;
}
let currentDir = (0, helpers_js_1.getParentDirPath)(dir);
const cache = closest_js_1.closestPatternCache.get(index_js_1.PACKAGE_JSON, fileSystem).get(topDir);
while (currentDir !== null) {
const file = cache.get(currentDir);
if (!file) {
return undefined;
}
const parsed = (0, parsed_dependency_files_js_1.parsePackageJson)(file);
if (parsed && hasCatalogs(parsed)) {
return parsed;
}
const fileDir = (0, files_js_1.dirnamePath)(file.filePath);
if (fileDir === topDir) {
return undefined;
}
currentDir = (0, helpers_js_1.getParentDirPath)(fileDir);
}
return undefined;
}
function hasCatalogs(packageJson) {
if (packageJson.catalog || packageJson.catalogs) {
return true;
}
const { workspaces } = packageJson;
if (workspaces && !Array.isArray(workspaces)) {
return !!(workspaces.catalog || workspaces.catalogs);
}
return false;
}