Skip to content

How to analyze actual code dependencies vs package.json dependencies in monorepo? #193

Description

@danielo515

Question: Analyzing Real Code Dependencies vs Package.json Dependencies

Hi! I'm working with a monorepo and trying to use skott to analyze which packages actually use which dependencies based on their code imports, rather than what's declared in their package.json files.

Current Situation

Currently, when I use getWorkspace(), it only returns dependencies listed in each package's package.json. However, in our monorepo setup, some packages use dependencies that are hoisted to the root or inherited from workspace dependencies, so they don't appear in the individual package.json files.

What I Want to Achieve

I want to create a map showing which packages actually use which dependencies based on their real code imports. For example:

{
  "effect": ["common", "dashboard", "jobs"],
  "zod": ["common", "dashboard"],
  "react": ["dashboard"]
}

This would show that the effect library is actually imported and used in the common, dashboard, and jobs packages, even if it's not listed in each package's individual package.json.

My Original Approach (Didn't Work)

I tried touse getWorkspace() to analyze the actual imports:

import { Record } from "effect";
import skott from "skott";

/**
 * Uses skott to find the dependencies that are used by each package in the workspace.
 * Returns a map of dependencies to the packages that use them, filtered to only include dependencies that are used by more than one package.
 */
async function main() {
  const { getWorkspace } = await skott();
  const packages = getWorkspace();
  const deps = Record.reduce(
    packages,
    {
      common: new Map<string, string[]>(),
    },
    (acc, pkg, key) => {
      console.error(key, pkg.dependencies);
      const deps = {
        ...pkg.dependencies,
        ...pkg.devDependencies,
        ...pkg.peerDependencies,
      };
      Record.map(deps, (version, name) => {
        const nameAndVersion = `${name}@${version}`;
        acc.common.set(nameAndVersion, [
          ...(acc.common.get(nameAndVersion) || []),
          key,
        ]);
      });
      return acc;
    }
  );
  return Record.filter(
    Object.fromEntries(deps.common.entries()),
    (_) => _.length >= 2
  );
}

main()
  .then((e) => process.stdout.write(JSON.stringify(e)))
  .catch(console.error);

However, this approach didn't seem to capture third-party dependencies based on real usage, just based on what it is in each package.json

Questions

  1. Is there a way to get actual import/require dependencies per file rather than just package.json dependencies?

  2. Does dependencyTracking: { thirdParty: true } add npm dependencies to the graph structure? If so, how should I access them?

  3. Is there a better approach to achieve what I'm trying to do with skott's current API?

  4. Would this be a useful feature to add to skott if it doesn't currently exist? Something like getActualDependenciesByPackage() that analyzes real code usage?

Monorepo Structure

apps/
  dashboard/     # Uses effect, react, etc.
  jobs/          # Uses effect, etc.
packages/
  common/        # Uses effect, zod, etc.

Each package may use dependencies that are hoisted to the root package.json or defined in the workspace catalog, so individual package.json files don't reflect actual usage.

Any guidance would be greatly appreciated! Thanks for the amazing tool! 🙏

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions