---
title: TurboSnap dependency tracing
description: Speed up tests by detecting file changes with Git
sidebar: { order: 5, label: "Dependency tracing" }
slug: "turbosnap/dependency-tracing"
---

# TurboSnap dependency tracing

TurboSnap examines your project's Git history and Webpack's dependency graph to determine which stories might be affected by changes. If you're new to these concepts, then this guide is for you.

It'll help you better understand dependency tracing, enabling you to use TurboSnap more effectively. You'll also learn how to trace dependencies in projects using Vite.

## Tracing dependencies with Webpack

To start, let's visit [Webpack's dependency graph concept](https://webpack.js.org/concepts/dependency-graph/):

> ❝ Any time one file depends on another, webpack treats this as a dependency. This allows webpack to take non-code assets, such as images or web fonts, and also provide them as dependencies for your application.
>
> When webpack processes your application, it starts from a list of modules defined on the command line or in its configuration file. Starting from these [entry points](https://webpack.js.org/concepts/entry-points/), webpack recursively builds a dependency graph that includes every module your application needs, then bundles all of those modules into a small number of bundles - often, only one - to be loaded by the browser. ❞

TurboSnap leverages [Webpack's Stats Data API](https://webpack.js.org/api/stats) to generate a JSON file with detailed statistics about a project's modules. These statistics allow TurboSnap to analyze a project's dependency graph by providing information on asset objects, related chunks (or grouped modules), and module dependencies linked to the assets.

If Chromatic builds your Storybook, it will automatically generate the stats file. But if you provide Chromatic with a prebuilt Storybook, you must add the `--stats-json` (or `--webpack` for Storybook versions 7 and lower) flag to the `build-storybook` command.

The stats file can be a bit challenging to read. Therefore, the Chromatic CLI offers a [`trim-stats-file`](/docs/configure/) option to make the file more human-readable. Use it like so:

```shell
npx chromatic trim-stats-file
```

Or, if you're using a custom build directory:

```shell
npx chromatic trim-stats-file ./path/to/preview-stats.json
```

### Reading the trimmed stats file

After running the `trim-stats-file` command, Chromatic will output a `preview-stats.trimmed.json` file. While this file is more readable, it can still be a bit daunting, so let's break it down with an example.

```json title="preview-stats.trimmed"
{
  "id": "./src/inputs/PinInput/PinInput.tsx",
  "name": "./src/inputs/PinInput/PinInput.tsx + 4 modules",
  "modules": [
    { "name": "./src/inputs/PinInput/PinInput.tsx" },
    { "name": "./src/inputs/PinInput/SeparatedPinInput.tsx" },
    { "name": "./src/inputs/PinInput/SinglePinInput.tsx" },
    { "name": "./src/inputs/utils.ts" },
    { "name": "./src/inputs/PinInput/PinCaret.tsx" }
  ],
  "reasons": [
    { "moduleName": "./src/data/DataGrid/cells/EditablePinInput.tsx" },
    { "moduleName": "./src/inputs/PinInput/index.ts" }
  ]
}
```

In this example, we have an asset with the `chunkName` of `./src/inputs/PinInput/PinInput.tsx + 4 modules`. There's a total of five modules within this chunk:

```
./src/inputs/PinInput/PinInput.tsx
./src/inputs/PinInput/SeparatedPinInput.tsx
./src/inputs/PinInput/SinglePinInput.tsx
./src/inputs/utils.ts
./src/inputs/PinInput/PinCaret.tsx
```

The `"reasons"` key provides information about the asset's dependency graph. In other words, modules that depend on this asset. Any changes to the asset chunk's modules may have an impact on its dependent modules:

```
./src/data/DataGrid/cells/EditablePinInput.tsx
./src/inputs/PinInput/index.ts
```

### Asset object for a story file

The asset object for a story file usually looks a bit different:

```json title="preview-stats.trimmed"
{
  "id": "./src/inputs/PinInput/stories/PinInput.stories.tsx",
  "name": "./src/inputs/PinInput/stories/PinInput.stories.tsx + 4 modules",
  "modules": [
    { "name": "./src/inputs/PinInput/stories/PinInput.stories.tsx" },
    {
      "name": "./src/inputs/PinInput/stories/PinInputWithNoCopyPaste.storyfile.tsx"
    },
    {
      "name": "./src/inputs/PinInput/stories/PinInputWithNoCopyPaste.storyfile.tsx?raw"
    },
    {
      "name": "./src/inputs/PinInput/stories/PinInputWithValidation.storyfile.tsx"
    },
    {
      "name": "./src/inputs/PinInput/stories/PinInputWithValidation.storyfile.tsx?raw"
    }
  ],
  "reasons": [
    {
      "moduleName": "./src/ lazy ^\\.\\/.*$ include: (?%21.*node_modules)(?:\\/src(?:\\/(?%21\\.)(?:(?:(?%21(?:^%7C\\/)\\.).)*?)\\/%7C\\/%7C$)(?%21\\.)(?=.)[^/]*?\\.stories\\.(js%7Cjsx%7Cts%7Ctsx))$ chunkName: [request] namespace object"
    }
  ]
}
```

Under "reasons," you'll notice a `moduleName` resembling a path pattern. In the full, untrimmed file, the `issuerPath`'s are `"./storybook-config-entry.js"` and `"./storybook-stories.js"`. That's because the dependency is related to this project's Storybook configuration:

```ts title=".storybook/main.ts"
import type { StorybookConfig } from "@storybook/react-webpack5";

const config: StorybookConfig = {
  stories: [
    {
      directory: "../",
      files: "**/*.@(mdx|stories.@(js|jsx|ts|tsx))",
    },
  ],
  // ...
};
export default config;
```

## Tracing dependencies with Vite

Vite support for TurboSnap is available out of the box, starting with Storybook 8.0 and later, and does not require any additional configuration. However, if you're using an older version of Storybook, you must install the [vite-plugin-turbosnap](https://github.com/IanVS/vite-plugin-turbosnap) plugin to enable TurboSnap support with Vite.

Similar to Webpack, when you run the `build-storybook` command, the `preview-stats.json` file is automatically generated. It contains information about each module being built and a mapping between each file and all the imported files. This structure mirrors that used by Wepack but only includes the information TurboSnap needs to perform dependency checks.

## Next: Use the stats file to trace dependencies

If you're trying to understand why a story file is being detected as changed, you can search for its file path in the trimmed stats file and trace its dependencies to see which modules were affected. Chromatic has a trace utility to simplify this process.

In this next chapter, learn how to use the trace utility to analyze your dependency graph. Identify and remove unnecessary imports, diagnose false positives from build tools, and understand the real propagation of changes for optimized bundles and confident refactoring.

<a class="btn primary round" href="/docs/turbosnap/trace-utility">
  Read next chapter
</a>

---

### Troubleshooting

<details>
<summary>How do circular dependencies impact tracing?</summary>

TurboSnap uses _tree-shaking_ to efficiently identify which files in your project have changed and how those changes propagate through your dependency graph. Tree-shaking may not work as intended when circular dependencies exist because the loop can obscure the true "consequences" of changes. Circular dependencies occur when two or more files depend on each other directly or indirectly, creating a loop. For example:

- `Button/index.ts` exports `Button.tsx`.

- If `Button.tsx` imports something from `Card/index.ts` (directly or indirectly), and `Card.tsx` imports something from `Button/index.ts`, a Circular Dependency is created.

When TurboSnap flags unchanged files as changed, you can use the [`--untraced`](/docs/turbosnap/setup#avoid-re-testing-dependent-stories-when-certain-files-changed) flag to correct its behavior by excluding certain files. However, restructuring your project to avoid circular dependencies may be a better, long-lasting solution.

</details>

<details>
<summary>Why transitive dependencies are not tracked?</summary>

_Transitive dependencies_ occur when a module indirectly depends on another through other modules. Here, module `C` is a transitive dependency of module `A` because it is imported into module `B`:

```
// Module A
import B from './B';

// Module B
import C from './C';
```

Sometimes, Webpack struggles to handle transitive dependencies effectively due to standard configurations, specific project structures, or dependency patterns, making it hard to identify the root cause of dependency tracking issues.

#### Using the `sideEffects` Property

Set the `sideEffects` property in your `package.json` to prevent accidental removal of dependencies and track side effects in third files. There are two ways to set it:

1. Set `sideEffects` to `true` to track all transitive dependencies:

```
"sideEffects": true
```

2. Specify `sideEffects` as an _array_ to explicitly list files or patterns:

```
"sideEffects": [
"**/*.css",
"**/*.scss",
"./src/global.js"
]
```

#### Testing the Configuration

To verify this solution, add a specific file with transitive imports to the `sideEffects` array and observe whether it behaves as expected.

Adjusting the `sideEffects` property can cause other unexpected consequences, so we recommend reviewing the [Webpack Documentation on Tree-Shaking](https://webpack.js.org/guides/tree-shaking/).

</details>