New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@wroud/di-tools-analyzer

Package Overview
Dependencies
Maintainers
0
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@wroud/di-tools-analyzer - npm Package Compare versions

Comparing version 0.2.1 to 0.3.0

lib/chart/createClusters.d.ts

10

CHANGELOG.md

@@ -6,2 +6,12 @@ <!-- header -->

<!-- version:0.3.0 -->
## 0.3.0 (2024-11-18)
[Compare changes](https://github.com/Wroud/foundation/compare/di-tools-analyzer-v0.2.1...di-tools-analyzer-v0.3.0)
<!-- changelog -->
### ✨ Features
- visualization for optional dependencies and clusters based on modules ([2af044e](https://github.com/Wroud/foundation/commit/2af044e))
<!-- version:0.2.1 -->

@@ -8,0 +18,0 @@ ## 0.2.1

73

lib/chart/createChart.js

@@ -1,5 +0,3 @@

import { forceLink } from "d3";
import { createSvg } from "./createSvg.js";
import { addDefs } from "./addDefs.js";
import { createForceSimulation } from "./createForceSimulation.js";
import { createLegend } from "./createLegend.js";

@@ -10,2 +8,4 @@ import { createLinks } from "./createLinks.js";

import { createFullScreenSwitch } from "./createFullScreenSwitch.js";
import { createDagreLayout } from "./createDagreLayout.js";
import { createClusters } from "./createClusters.js";
export function createChart(svgElement, width, height) {

@@ -19,3 +19,3 @@ const resizeSubscribers = [];

const fullScreenSwitch = createFullScreenSwitch(svg, defs);
const legend = createLegend(svg);
const legend = createLegend(svg, defs);
resizeSubscribers.push(() => {

@@ -25,4 +25,13 @@ legend.update();

});
const simulation = createForceSimulation().on("tick", ticked);
const links = createLinks(paper.element, defs);
const dagreLayout = createDagreLayout();
// const forceLayout = createForceLayout({
// onTick() {
// ticked();
// },
// });
const clusters = createClusters(paper.element, {});
const links = createLinks(paper.element, defs, {
onHover: (link) => links.highlightLink(link),
onBlur: () => links.resetHighlight(),
});
const nodes = createNodes(paper.element, {

@@ -32,6 +41,11 @@ onHover: (node) => links.highlight(node),

onDragStart: () => {
simulation.alphaTarget(0.3).restart();
// forceLayout.start();
ticked();
},
onDrag: () => {
ticked();
},
onDragEnd: () => {
simulation.alphaTarget(0);
// forceLayout.stop();
ticked();
},

@@ -47,16 +61,37 @@ });

// recycling old nodes to preserve position and velocity.
const old = new Map(nodes.nodes.data().map((d) => [d.data.id, d]));
const nodesData = graph.nodes.map((d) => ({ ...old.get(d.id), data: d }));
const linksData = graph.links.map((d) => ({ ...d }));
const oldNodes = new Map(nodes.nodes.data().map((d) => [d.data.id, d]));
const oldLinks = new Map(links.links.data().map((d) => [d.data.source + ":" + d.data.target, d]));
const nodesData = graph.nodes.map((d) => ({
...oldNodes.get(d.id),
data: d,
}));
const linksData = graph.links.map((d) => ({
...oldLinks.get(d.source + ":" + d.target),
data: d,
points: [],
source: nodesData.find((n) => n.data.id === d.source),
target: nodesData.find((n) => n.data.id === d.target),
}));
const layout = dagreLayout.updateData(nodesData, linksData);
// forceLayout.updateData(nodesData, linksData);
const { width, height } = layout.getDemensions();
if (width && height) {
paper.translate(width / 2, height / 2);
paper.zoom(0.8);
}
for (const node of nodesData) {
const { x, y } = layout.getNode(node.data.id);
node.x = x;
node.y = y;
}
for (const link of linksData) {
const { points } = layout.getEdge(link.data.source, link.data.target);
link.points = points.map(({ x, y }) => ({ x, y }));
}
nodes.updateData(nodesData);
links.updateData(linksData);
simulation
.nodes(nodesData)
.force("link", forceLink(linksData)
.id((d) => d.data.id)
.distance(50)
.strength(0.4))
.alpha(1)
.restart()
.tick();
clusters.updateData(layout.getClusters());
nodes.update();
links.update();
clusters.update();
},

@@ -63,0 +98,0 @@ };

import { type Selection } from "d3";
export declare function createLegend(svg: Selection<SVGSVGElement, unknown, null, undefined>): {
import type { IDefs } from "./addDefs.js";
export declare function createLegend(svg: Selection<SVGSVGElement, unknown, null, undefined>, defs: IDefs): {
update(): void;
};
//# sourceMappingURL=createLegend.d.ts.map
import { select } from "d3";
import { Layout } from "./Layout.js";
export function createLegend(svg) {
export function createLegend(svg, defs) {
// Legend data

@@ -10,6 +10,8 @@ const legendData = [

{ label: "Not Found", class: "node-not-found" },
{ label: "Dependency", class: "", type: "link" },
{ label: "Optional", class: "optional-link", type: "link" },
];
// Calculate the dimensions for the legend background
const legendItemHeight = 20;
const legendWidth = 90;
const legendWidth = 120;
const legendHeight = legendData.length * legendItemHeight;

@@ -42,7 +44,22 @@ const legend = svg.append("g").attr("style", "transform-origin: 100% 0%;");

g.classed(d.class, true);
g.append("circle")
.attr("r", Layout.node.radius)
.classed("node-circle", true);
if (d.type === "link") {
g.append("line")
.classed("link", true)
.classed(d.class, true)
.attr("opacity", Layout.link.opacity)
.attr("marker-end", `url(#${defs.defs.arrowHead})`)
.style("stroke-dasharray", d.class === "optional-link" ? Layout.linkOptional.dashArray : "")
.attr("x1", -Layout.node.radius)
.attr("y1", 0)
.attr("x2", Layout.node.radius * 3)
.attr("y2", 0);
}
else {
g.append("circle")
.attr("r", Layout.node.radius)
.attr("cx", Layout.node.radius)
.classed("node-circle", true);
}
g.append("text")
.attr("x", 10)
.attr("x", 20)
.attr("y", 5)

@@ -49,0 +66,0 @@ .text(d.label)

@@ -1,12 +0,25 @@

import { type Selection } from "d3";
import * as d3 from "d3";
import type { IDefs } from "./addDefs.js";
import { type INodeDatum } from "./createNodes.js";
export type ILinkDatum = d3.SimulationLinkDatum<INodeDatum>;
export declare function createLinks(paper: Selection<SVGGElement, unknown, null, undefined>, defs: IDefs): {
readonly links: Selection<import("d3-selection").BaseType, ILinkDatum, SVGGElement, unknown>;
import type { ILink } from "../IGraph.js";
export type ILinkDatum = {
data: ILink;
points: Array<{
x: number;
y: number;
}>;
source: INodeDatum;
target: INodeDatum;
};
export declare function createLinks(paper: d3.Selection<SVGGElement, unknown, null, undefined>, defs: IDefs, events?: {
onHover?: (link: ILinkDatum) => void;
onBlur?: (link: ILinkDatum) => void;
}): {
readonly links: d3.Selection<d3.BaseType, ILinkDatum, SVGGElement, unknown>;
updateData(data: ILinkDatum[]): void;
update(): void;
highlight(source: INodeDatum): void;
highlightLink(link: ILinkDatum): void;
resetHighlight(): void;
};
//# sourceMappingURL=createLinks.d.ts.map

@@ -1,5 +0,6 @@

import { select } from "d3";
import * as d3 from "d3";
import {} from "./createNodes.js";
import { Layout } from "./Layout.js";
export function createLinks(paper, defs) {
import { getLinkPoints } from "./getLinkPoints.js";
export function createLinks(paper, defs, events) {
let link = paper

@@ -9,4 +10,13 @@ .append("g")

.attr("stroke", "#999")
.selectAll("line")
.selectAll("g")
.data([]);
function hoverLink() {
function entered(event, d) {
events?.onHover?.(d);
}
function left(event, d) {
events?.onBlur?.(d);
}
return { entered, left };
}
return {

@@ -18,31 +28,59 @@ get links() {

link = link
.data(data, (d) => [d.source, d.target])
.join((enter) => enter
.append("line")
.classed("link", true)
.attr("opacity", Layout.link.opacity)
.attr("marker-end", `url(#${defs.defs.arrowHead})`));
.data(data, (data) => data.data.source + ":" + data.data.target)
.join((enter) => {
const line = enter
.append("g")
.call((group) => {
group
.append("path")
.classed("link", true)
.classed("optional-link", (data) => data.data.optional)
.attr("opacity", Layout.link.opacity)
.attr("marker-end", `url(#${defs.defs.arrowHead})`)
.style("stroke-dasharray", (data) => data.data.optional ? Layout.linkOptional.dashArray : null);
})
.call((group) => {
group
.append("path")
.classed("link-hit-area", true)
.attr("opacity", 0)
.attr("stroke-width", 10)
.call((line) => {
line.append("title").text((data) => data.data.name);
})
.on("mouseenter", hoverLink().entered)
.on("mouseleave", hoverLink().left);
});
return line;
});
},
update() {
this.links.each(function update(datum) {
const link = select(this);
const target = datum.target;
const source = datum.source;
const dx = target.x - source.x;
const dy = target.y - source.y;
const distance = Math.sqrt(dx * dx + dy * dy);
const offsetX = (dx * Layout.node.radius) / distance;
const offsetY = (dy * Layout.node.radius) / distance;
link
.attr("x1", source.x + offsetX)
.attr("y1", source.y + offsetY)
.attr("x2", target.x - offsetX)
.attr("y2", target.y - offsetY);
const link = d3
.select(this)
.selectAll("path");
if (datum.points.length === 0) {
datum.points = getLinkPoints(datum);
}
link.attr("d", (d) => d3
.line()
.x((point) => point[0])
.y((point) => point[1])
.curve(d3.curveMonotoneY)(d.points.map((p) => [p.x || 0, p.y || 0])));
});
},
highlight(source) {
this.links.attr("opacity", (o) => o.source === source ? Layout.link.hoverOpacity : Layout.link.opacity);
this.links
.selectAll("path.link")
.attr("opacity", (o) => o.source === source ? Layout.link.hoverOpacity : Layout.link.opacity);
},
highlightLink(link) {
this.links
.selectAll("path.link")
.attr("opacity", (o) => o === link ? Layout.link.hoverOpacity : Layout.link.opacity);
},
resetHighlight() {
this.links.attr("opacity", Layout.link.opacity);
this.links
.selectAll("path.link")
.attr("opacity", Layout.link.opacity);
},

@@ -49,0 +87,0 @@ };

@@ -1,18 +0,19 @@

import { type Selection, type SimulationNodeDatum } from "d3";
import * as d3 from "d3";
import type { INode } from "../IGraph.js";
export type INodeDatum = SimulationNodeDatum & {
export type INodeDatum = d3.SimulationNodeDatum & {
data: INode;
};
export interface INodes {
nodes: Selection<SVGGElement, INodeDatum, SVGSVGElement, unknown>;
nodes: d3.Selection<SVGGElement, INodeDatum, SVGSVGElement, unknown>;
updateData(data: INodeDatum[]): void;
update(): void;
}
export declare function createNodes(paper: Selection<SVGGElement, unknown, null, undefined>, events?: {
export declare function createNodes(paper: d3.Selection<SVGGElement, unknown, null, undefined>, events?: {
onHover?: (node: INodeDatum) => void;
onBlur?: (node: INodeDatum) => void;
onDragStart?: (node: INodeDatum) => void;
onDrag?: (node: INodeDatum) => void;
onDragEnd?: (node: INodeDatum) => void;
}): {
readonly nodes: Selection<SVGGElement, INodeDatum, SVGGElement, unknown>;
readonly nodes: d3.Selection<SVGGElement, INodeDatum, SVGGElement, unknown>;
updateData(data: INodeDatum[]): void;

@@ -19,0 +20,0 @@ update(): void;

@@ -1,2 +0,2 @@

import { drag } from "d3";
import * as d3 from "d3";
import { Layout } from "./Layout.js";

@@ -29,2 +29,3 @@ import { ServiceLifetime } from "@wroud/di/di/ServiceLifetime.js";

function dragged(event, d) {
events?.onDrag?.(d);
d.fx = event.x;

@@ -40,3 +41,4 @@ d.fy = event.y;

}
return drag()
return d3
.drag()
.on("start", dragstarted)

@@ -58,3 +60,43 @@ .on("drag", dragged)

.text((d) => d.data.name + (d.data.notFound ? " (not found)" : ""));
node.selectChild("text").text((d) => d.data.name);
node.selectChild("text").each(function (d) {
const textElement = d3.select(this);
const maxLength = 30;
const lineHeight = 1.2;
const processText = (text) => {
if (text.includes(" ")) {
const words = text.split(" ");
const lines = [];
let currentLine = "";
for (const word of words) {
if (currentLine.length + word.length + 1 <= maxLength) {
currentLine += word;
}
else {
if (currentLine.length > 0) {
lines.push(currentLine);
}
currentLine = word;
}
}
if (currentLine.length) {
lines.push(currentLine);
}
return lines;
}
else {
return text.length > maxLength
? [text.slice(0, maxLength - 3) + "..."]
: [text];
}
};
const lines = processText(d.data.name);
textElement.text(null);
lines.reverse().forEach((line, i) => {
textElement
.append("tspan")
.text(line)
.attr("x", 0)
.attr("dy", i === 0 ? -16 : `-${lineHeight}em`);
});
});
}

@@ -88,3 +130,4 @@ return {

.attr("stroke", "white")
.attr("paint-order", "stroke");
.attr("paint-order", "stroke")
.attr("dominant-baseline", "hanging");
updateNode(node);

@@ -91,0 +134,0 @@ return node;

import { type Selection } from "d3";
export declare function createPaper(svg: Selection<SVGSVGElement, unknown, null, undefined>): {
readonly element: Selection<SVGGElement, unknown, null, undefined>;
center(): void;
translate(x: number, y: number): void;
zoom(scale: number): void;
};
//# sourceMappingURL=createPaper.d.ts.map

@@ -1,10 +0,9 @@

import { zoom } from "d3";
import { zoom, zoomIdentity } from "d3";
export function createPaper(svg) {
const g = svg.append("g");
// const { width, height } = svg.node()!.getBoundingClientRect();
svg.call(zoom()
const zoomBehavior = zoom()
.extent([
[0, 0],
[0, 0],
// [width, height],
])

@@ -16,4 +15,4 @@ .scaleExtent([0.1, 2])

return svgFocused;
}));
svg.attr("tabindex", 0);
});
svg.call(zoomBehavior).attr("tabindex", 0);
function zoomed({ transform }) {

@@ -26,4 +25,18 @@ g.attr("transform", transform.toString());

},
center() {
const { width, height } = svg.node().getBoundingClientRect();
const x = width / 2;
const y = height / 2;
svg
.transition()
.call(zoomBehavior.transform, zoomIdentity.translate(x, y));
},
translate(x, y) {
svg.call(zoomBehavior.translateTo, x, y);
},
zoom(scale) {
svg.call(zoomBehavior.scaleTo, scale);
},
};
}
//# sourceMappingURL=createPaper.js.map

@@ -9,3 +9,6 @@ export declare const Layout: {

};
linkOptional: {
dashArray: string;
};
};
//# sourceMappingURL=Layout.d.ts.map

@@ -9,3 +9,6 @@ export const Layout = {

},
linkOptional: {
dashArray: "4 2",
},
};
//# sourceMappingURL=Layout.js.map
import { type IServiceCollection } from "@wroud/di";
import type { IGraph } from "./IGraph.js";
export declare function getDependenciesGraph(serviceCollection: IServiceCollection): Promise<IGraph>;
import type { ServiceCollectionProxy } from "./ServiceCollectionProxy.js";
export declare function getDependenciesGraph(serviceCollection: IServiceCollection, proxy?: ServiceCollectionProxy): Promise<IGraph>;
//# sourceMappingURL=getDependenciesGraph.d.ts.map

@@ -1,9 +0,10 @@

import { ServiceRegistry } from "@wroud/di";
import {} from "@wroud/di";
import { v4 as uuid } from "uuid";
import { getNameOfServiceType } from "@wroud/di/helpers/getNameOfServiceType.js";
import { loadImplementation } from "./loadImplementation.js";
import { getDeps } from "./loadImplementation.js";
import { getNameOfDescriptor } from "@wroud/di/helpers/getNameOfDescriptor.js";
import { ServiceLifetime } from "@wroud/di/di/ServiceLifetime.js";
import { getServiceTypeFromDependency } from "@wroud/di/helpers/getServiceTypeFromDependency.js";
export async function getDependenciesGraph(serviceCollection) {
import { isOptionalDependency } from "./isOptionalDependency.js";
export async function getDependenciesGraph(serviceCollection, proxy) {
const nodes = new Map();

@@ -27,3 +28,3 @@ const links = [];

async function addDescriptorNode(descriptor) {
await loadImplementation(descriptor);
await getDeps(descriptor);
const node = nodes.get(descriptor);

@@ -37,2 +38,6 @@ if (!node) {

};
const module = proxy?.getModules().get(descriptor);
if (module) {
node.module = module;
}
nodes.set(descriptor, node);

@@ -44,9 +49,5 @@ return node;

for (const descriptor of serviceCollection) {
const implementation = await loadImplementation(descriptor);
const deps = await getDeps(descriptor);
const source = await addDescriptorNode(descriptor);
const dependencies = ServiceRegistry.get(implementation);
if (!dependencies) {
continue;
}
for (const dependency of dependencies.dependencies) {
for (const dependency of deps) {
const service = getServiceTypeFromDependency(dependency);

@@ -60,2 +61,3 @@ const descriptors = serviceCollection.getDescriptors(service);

name: `${source.name} -> ${target.name}`,
optional: isOptionalDependency(dependency),
});

@@ -65,2 +67,5 @@ continue;

for (const dep of descriptors) {
if (dep === descriptor) {
continue;
}
const target = await addDescriptorNode(dep);

@@ -71,2 +76,3 @@ links.push({

name: `${source.name} -> ${target.name}`,
optional: isOptionalDependency(dependency),
});

@@ -73,0 +79,0 @@ }

@@ -6,2 +6,3 @@ import type { ServiceLifetime } from "@wroud/di/di/ServiceLifetime.js";

lifetime: ServiceLifetime;
module?: string;
notFound?: boolean;

@@ -13,2 +14,3 @@ }

name: string;
optional: boolean;
}

@@ -15,0 +17,0 @@ export interface IGraph {

export * from "./getDependenciesGraph.js";
export * from "./ServiceCollectionProxy.js";
export * from "./chart/createChart.js";

@@ -3,0 +4,0 @@ export * from "./IGraph.js";

export * from "./getDependenciesGraph.js";
export * from "./ServiceCollectionProxy.js";
export * from "./chart/createChart.js";

@@ -3,0 +4,0 @@ export * from "./IGraph.js";

@@ -1,3 +0,3 @@

import type { IServiceDescriptor } from "@wroud/di/types";
export declare function loadImplementation<T>(descriptor: IServiceDescriptor<T>): Promise<import("@wroud/di/types").ISyncServiceImplementation<T>>;
import type { IResolverServiceType, IServiceDescriptor } from "@wroud/di/types";
export declare function getDeps<T>(descriptor: IServiceDescriptor<T>): Promise<IResolverServiceType<any, any>[]>;
//# sourceMappingURL=loadImplementation.d.ts.map

@@ -1,9 +0,10 @@

import { isAsyncServiceImplementationLoader } from "@wroud/di/helpers/isAsyncServiceImplementationLoader.js";
export async function loadImplementation(descriptor) {
let implementation = descriptor.implementation;
if (isAsyncServiceImplementationLoader(implementation)) {
implementation = await implementation.load();
}
return implementation;
import { resolveGeneratorAsync } from "@wroud/di/helpers/resolveGeneratorAsync.js";
export async function getDeps(descriptor) {
const deps = [];
await resolveGeneratorAsync(descriptor.resolver.resolve(function* gen(dep, reqBy, mode) {
deps.push(dep);
return null;
}, descriptor, new Set(), "async"));
return deps;
}
//# sourceMappingURL=loadImplementation.js.map
{
"name": "@wroud/di-tools-analyzer",
"description": "DI Tools Analyzer is a tool that helps you to analyze the dependency injection tools in your project and generate a report for them.",
"version": "0.2.1",
"version": "0.3.0",
"packageManager": "yarn@4.5.0",

@@ -13,3 +13,3 @@ "type": "module",

"url": "https://github.com/Wroud/foundation",
"directory": "packages/di-tools-analyzer"
"directory": "packages/@wroud/di-tools-analyzer"
},

@@ -21,6 +21,4 @@ "sideEffects": [],

".": "./lib/index.js",
"./*": "./lib/*",
"./package.json": "./package.json",
"./src": "./src/index.ts",
"./src/*": "./src/*"
"./*.js": "./lib/*.js",
"./package.json": "./package.json"
},

@@ -33,2 +31,3 @@ "files": [

"lib",
"!lib/**/*.d.ts.map",
"!lib/**/*.test.js",

@@ -53,2 +52,3 @@ "!lib/**/*.test.d.ts",

"dependencies": {
"@dagrejs/dagre": "^1",
"@wroud/di": "^0",

@@ -55,0 +55,0 @@ "d3": "^7",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc