New:Socket for Asana Is Now Available.Learn more
Sign In

@changesets/get-dependents-graph

Package Overview
Dependencies
Maintainers
2
Versions
36
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@changesets/get-dependents-graph - npm Package Compare versions

Comparing version
3.0.0-next.3
to
3.0.0-next.4
+9
dist/index.d.mts
import { Packages } from "@changesets/types";
//#region src/index.d.ts
declare function getDependentsGraph(packages: Packages, opts?: {
ignoreDevDependencies?: boolean;
bumpVersionsWithWorkspaceProtocolOnly?: boolean;
}): Map<any, any>;
//#endregion
export { getDependentsGraph };
import path from "node:path";
import { styleText } from "node:util";
import Range from "semver/classes/range.js";
//#region ../color/src/index.ts
var src_default = new Proxy({}, { get(target, color) {
target[color] ??= (text) => styleText(color, text);
return target[color];
} });
//#endregion
//#region src/get-dependency-graph.ts
const DEPENDENCY_TYPES = [
"dependencies",
"devDependencies",
"peerDependencies",
"optionalDependencies"
];
const getAllDependencies = (config, ignoreDevDependencies) => {
const allDependencies = /* @__PURE__ */ new Map();
for (const type of DEPENDENCY_TYPES) {
const deps = config[type];
if (!deps) continue;
for (const name of Object.keys(deps)) {
const depRange = deps[name];
if (type === "devDependencies" && (ignoreDevDependencies || depRange.startsWith("link:") || depRange.startsWith("file:"))) continue;
allDependencies.set(name, depRange);
}
}
return allDependencies;
};
const isProtocolRange = (range) => range.indexOf(":") !== -1;
const getValidRange = (potentialRange) => {
if (isProtocolRange(potentialRange)) return null;
try {
return new Range(potentialRange);
} catch {
return null;
}
};
function getDependencyGraph(packages, rootPackage, { ignoreDevDependencies = false, bumpVersionsWithWorkspaceProtocolOnly = false } = {}) {
const graph = /* @__PURE__ */ new Map();
let valid = true;
const packagesByName = { [rootPackage.packageJson.name]: rootPackage };
const relativePathsByName = { [rootPackage.packageJson.name]: "." };
const queue = [rootPackage];
for (const pkg of packages.packages) {
queue.push(pkg);
packagesByName[pkg.packageJson.name] = pkg;
relativePathsByName[pkg.packageJson.name] = path.relative(rootPackage.dir, pkg.dir).replace(/\\/g, "/");
}
for (const pkg of queue) {
const { name } = pkg.packageJson;
const dependencies = [];
const allDependencies = getAllDependencies(pkg.packageJson, ignoreDevDependencies);
for (let [depName, depRange] of allDependencies) {
const match = packagesByName[depName];
if (!match) continue;
const expected = match.packageJson.version;
const rawDepRange = depRange;
if (depRange.startsWith("workspace:")) {
depRange = depRange.replace(/^workspace:/, "");
if (depRange === "*" || depRange === "^" || depRange === "~") {
dependencies.push(depName);
continue;
}
if (path.posix.normalize(depRange) === relativePathsByName[depName]) {
dependencies.push(depName);
continue;
}
if (!getValidRange(depRange)) {
valid = false;
console.error(`Package ${src_default.blue(name)} must depend on the current version of ${src_default.blue(depName)}: ${src_default.green(expected)} vs ${src_default.red(rawDepRange)}`);
continue;
}
} else if (bumpVersionsWithWorkspaceProtocolOnly) continue;
const range = getValidRange(depRange);
if (range && !range.test(expected) || isProtocolRange(depRange)) {
valid = false;
console.error(`Package ${src_default.blue(name)} must depend on the current version of ${src_default.blue(depName)}: ${src_default.green(expected)} vs ${src_default.red(rawDepRange)}`);
continue;
}
if (!range) continue;
dependencies.push(depName);
}
graph.set(name, {
pkg,
dependencies
});
}
return {
graph,
valid
};
}
//#endregion
//#region src/index.ts
function getDependentsGraph(packages, opts) {
const graph = /* @__PURE__ */ new Map();
const rootPackage = packages.rootPackage;
if (rootPackage == null) return /* @__PURE__ */ new Map();
const { graph: dependencyGraph } = getDependencyGraph(packages, rootPackage, opts);
const dependentsLookup = { [rootPackage.packageJson.name]: {
pkg: rootPackage,
dependents: []
} };
packages.packages.forEach((pkg) => {
dependentsLookup[pkg.packageJson.name] = {
pkg,
dependents: []
};
});
packages.packages.forEach((pkg) => {
const dependent = pkg.packageJson.name;
const valFromDependencyGraph = dependencyGraph.get(dependent);
if (valFromDependencyGraph) valFromDependencyGraph.dependencies.forEach((dependency) => {
dependentsLookup[dependency].dependents.push(dependent);
});
});
Object.keys(dependentsLookup).forEach((key) => {
graph.set(key, dependentsLookup[key]);
});
const simplifiedDependentsGraph = /* @__PURE__ */ new Map();
graph.forEach((pkgInfo, pkgName) => {
simplifiedDependentsGraph.set(pkgName, pkgInfo.dependents);
});
return simplifiedDependentsGraph;
}
//#endregion
export { getDependentsGraph };
import { defineConfig } from "tsdown/config";
import { baseConfig } from "../../tsdown.config.ts";
export default defineConfig(baseConfig);
+9
-0
# @changesets/get-dependents-graph
## 3.0.0-next.4
### Patch Changes
- [#2004](https://github.com/changesets/changesets/pull/2004) [`169b128`](https://github.com/changesets/changesets/commit/169b128522f0e53ef228f3acd8118709b0f72156) Thanks [@ghostdevv](https://github.com/ghostdevv)! - Replace `picocolors` with `node:util`'s `styleText`
- Updated dependencies [[`062530b`](https://github.com/changesets/changesets/commit/062530b825d53abc9d8934f3a50cc61ff3ff82b8)]:
- @changesets/types@7.0.0-next.4
## 3.0.0-next.3

@@ -4,0 +13,0 @@

+10
-6
{
"name": "@changesets/get-dependents-graph",
"version": "3.0.0-next.3",
"version": "3.0.0-next.4",
"description": "Get the graph of dependents in a monorepo",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/changesets/changesets.git",
"directory": "packages/get-dependents-graph"
},
"type": "module",
"exports": {
".": "./dist/changesets-get-dependents-graph.js",
".": "./dist/index.mjs",
"./package.json": "./package.json"
},
"license": "MIT",
"repository": "https://github.com/changesets/changesets/tree/main/packages/get-dependents-graph",
"dependencies": {
"picocolors": "^1.1.0",
"semver": "^7.5.3",
"@changesets/types": "^7.0.0-next.3"
"@changesets/types": "^7.0.0-next.4"
},
"devDependencies": {
"vitest": "^4.1.5",
"@changesets/color": "^1.0.0",
"@changesets/test-utils": "0.0.9-next.3"

@@ -20,0 +24,0 @@ },

@@ -132,3 +132,3 @@ import path from "node:path";

).toBe(
`Package "foo" must depend on the current version of "bar": "1.0.0" vs "link:../bar"`,
`Package foo must depend on the current version of bar: 1.0.0 vs link:../bar`,
);

@@ -176,3 +176,3 @@ }),

).toMatchInlineSnapshot(
`"Package "foo" must depend on the current version of "bar": "1.0.0" vs "0.9.0""`,
`"Package foo must depend on the current version of bar: 1.0.0 vs 0.9.0"`,
);

@@ -302,3 +302,3 @@ }),

).toMatchInlineSnapshot(
`"Package "foo" must depend on the current version of "bar": "1.0.0" vs "workspace:packages/not-bar""`,
`"Package foo must depend on the current version of bar: 1.0.0 vs workspace:packages/not-bar"`,
);

@@ -305,0 +305,0 @@ }),

import path from "node:path";
import c from "@changesets/color";
import type { Package, Packages, PackageJSON } from "@changesets/types";
import pc from "picocolors";
import Range from "semver/classes/range.js";

@@ -124,8 +124,5 @@

valid = false;
// TODO: replace with returning errors/warnings
console.error(
`Package ${pc.cyan(
`"${name}"`,
)} must depend on the current version of ${pc.cyan(
`"${depName}"`,
)}: ${pc.green(`"${expected}"`)} vs ${pc.red(`"${rawDepRange}"`)}`,
`Package ${c.blue(name)} must depend on the current version of ${c.blue(depName)}: ${c.green(expected)} vs ${c.red(rawDepRange)}`,
);

@@ -142,8 +139,5 @@ continue;

valid = false;
// TODO: replace with returning errors/warnings
console.error(
`Package ${pc.cyan(
`"${name}"`,
)} must depend on the current version of ${pc.cyan(
`"${depName}"`,
)}: ${pc.green(`"${expected}"`)} vs ${pc.red(`"${rawDepRange}"`)}`,
`Package ${c.blue(name)} must depend on the current version of ${c.blue(depName)}: ${c.green(expected)} vs ${c.red(rawDepRange)}`,
);

@@ -150,0 +144,0 @@ continue;

export * from "./declarations/src/index.js";
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hhbmdlc2V0cy1nZXQtZGVwZW5kZW50cy1ncmFwaC5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi9kZWNsYXJhdGlvbnMvc3JjL2luZGV4LmQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEifQ==
import path from 'node:path';
import pc from 'picocolors';
import Range from 'semver/classes/range.js';
const DEPENDENCY_TYPES = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
const getAllDependencies = (config, ignoreDevDependencies) => {
const allDependencies = new Map();
for (const type of DEPENDENCY_TYPES) {
const deps = config[type];
if (!deps) continue;
for (const name of Object.keys(deps)) {
const depRange = deps[name];
if (type === "devDependencies" && (ignoreDevDependencies || depRange.startsWith("link:") || depRange.startsWith("file:"))) {
continue;
}
allDependencies.set(name, depRange);
}
}
return allDependencies;
};
const isProtocolRange = range => range.indexOf(":") !== -1;
const getValidRange = potentialRange => {
if (isProtocolRange(potentialRange)) {
return null;
}
try {
return new Range(potentialRange);
} catch {
return null;
}
};
function getDependencyGraph(packages, rootPackage, {
ignoreDevDependencies = false,
bumpVersionsWithWorkspaceProtocolOnly = false
} = {}) {
const graph = new Map();
let valid = true;
const packagesByName = {
[rootPackage.packageJson.name]: rootPackage
};
const relativePathsByName = {
[rootPackage.packageJson.name]: "."
};
const queue = [rootPackage];
for (const pkg of packages.packages) {
queue.push(pkg);
packagesByName[pkg.packageJson.name] = pkg;
relativePathsByName[pkg.packageJson.name] = path.relative(rootPackage.dir, pkg.dir).replace(/\\/g, "/");
}
for (const pkg of queue) {
const {
name
} = pkg.packageJson;
const dependencies = [];
const allDependencies = getAllDependencies(pkg.packageJson, ignoreDevDependencies);
// eslint-disable-next-line prefer-const
for (let [depName, depRange] of allDependencies) {
const match = packagesByName[depName];
if (!match) continue;
const expected = match.packageJson.version;
const rawDepRange = depRange;
const usesWorkspaceRange = depRange.startsWith("workspace:");
if (usesWorkspaceRange) {
depRange = depRange.replace(/^workspace:/, "");
if (depRange === "*" || depRange === "^" || depRange === "~") {
dependencies.push(depName);
continue;
}
if (path.posix.normalize(depRange) === relativePathsByName[depName]) {
dependencies.push(depName);
continue;
}
if (!getValidRange(depRange)) {
valid = false;
console.error(`Package ${pc.cyan(`"${name}"`)} must depend on the current version of ${pc.cyan(`"${depName}"`)}: ${pc.green(`"${expected}"`)} vs ${pc.red(`"${rawDepRange}"`)}`);
continue;
}
} else if (bumpVersionsWithWorkspaceProtocolOnly) {
continue;
}
const range = getValidRange(depRange);
if (range && !range.test(expected) || isProtocolRange(depRange)) {
valid = false;
console.error(`Package ${pc.cyan(`"${name}"`)} must depend on the current version of ${pc.cyan(`"${depName}"`)}: ${pc.green(`"${expected}"`)} vs ${pc.red(`"${rawDepRange}"`)}`);
continue;
}
// `depRange` could have been a tag and if a tag has been used there might have been a reason for that
// we should not count this as a local monorepro dependant
if (!range) {
continue;
}
dependencies.push(depName);
}
graph.set(name, {
pkg,
dependencies
});
}
return {
graph,
valid
};
}
function getDependentsGraph(packages, opts) {
const graph = new Map();
const rootPackage = packages.rootPackage;
if (rootPackage == null) {
return new Map();
}
const {
graph: dependencyGraph
} = getDependencyGraph(packages, rootPackage, opts);
const dependentsLookup = {
[rootPackage.packageJson.name]: {
pkg: rootPackage,
dependents: []
}
};
packages.packages.forEach(pkg => {
dependentsLookup[pkg.packageJson.name] = {
pkg,
dependents: []
};
});
packages.packages.forEach(pkg => {
const dependent = pkg.packageJson.name;
const valFromDependencyGraph = dependencyGraph.get(dependent);
if (valFromDependencyGraph) {
const dependencies = valFromDependencyGraph.dependencies;
dependencies.forEach(dependency => {
dependentsLookup[dependency].dependents.push(dependent);
});
}
});
Object.keys(dependentsLookup).forEach(key => {
graph.set(key, dependentsLookup[key]);
});
const simplifiedDependentsGraph = new Map();
graph.forEach((pkgInfo, pkgName) => {
simplifiedDependentsGraph.set(pkgName, pkgInfo.dependents);
});
return simplifiedDependentsGraph;
}
export { getDependentsGraph };
import type { Packages } from "@changesets/types";
export declare function getDependentsGraph(packages: Packages, opts?: {
ignoreDevDependencies?: boolean;
bumpVersionsWithWorkspaceProtocolOnly?: boolean;
}): Map<any, any>;