Skip to content
Fallow home
All docs pages

fallow list

See what fallow found in your project, so you can debug the configuration and check the project setup. Fallow list shows discovered files, entry points, active plugins, boundary zones, workspaces, and the startup import weight of each entry.

When a result looks wrong, first check what fallow found in your project. fallow list shows the files, entry points, plugins, and boundary zones that fallow uses. Use it to debug the configuration, or to confirm that fallow sees the correct project setup.

fallow list

Options

FlagDescription
--filesShow all discovered files
--entry-pointsShow entry point files
--pluginsShow active plugins
--boundariesShow architecture boundary zones, rules, and per-zone file counts
--workspacesShow monorepo workspaces and workspace-discovery diagnostics, for example a malformed package.json, an unreachable glob match, or a missing tsconfig reference. The fallow workspaces alias does the same.
--entry-weightShow the startup import weight of each runtime entry point, in source bytes. See Startup import weight.
-f, --format <FORMAT>Output format: human (default), json
[PATH]Show only the items in this file or directory. Fallow still builds the whole project, and activates the plugins from the whole project, as the analysis does. For example, fallow list src/app --entry-points lists only the entry points under src/app.

Entry points

fallow list --entry-points shows the entry points that the analysis uses. The listing runs the same framework plugins and the same entry-point discovery as fallow dead-code, so the two agree. For example, on a Nuxt project with autoImports on, a component that nothing renders is not an entry point in the listing, and fallow dead-code reports it as an unused file. The MCP project_info tool gives the same list.

When a framework plugin cannot use a config, the listing records it in workspace_diagnostics[], with the same plugin-config-unreadable and plugin-effect-not-modeled entries as the analysis. A --plugins or --entry-points listing has the array only when the plugin stage recorded an entry. See Debugging for the diagnostic kinds.

Startup import weight

fallow list --entry-weight shows how much code each runtime entry point loads before it runs. Use it to find the imports that make startup slow, and to keep that number from growing.

For each runtime entry point, fallow walks the import graph and puts every project module that the entry reaches into one of three groups:

GroupWhich modules
EagerModules that load before the entry runs: a static import, export ... from, require(), require.context, and import.meta.glob(..., { eager: true }).
DeferredModules that load only on demand: import('./x'), a template import(), and a lazy import.meta.glob. A module that is also eager is not deferred.
Out of threadModules that only another thread or process loads: a new URL('./x.ts', import.meta.url) reference (for example a worker URL), a webpack inline request through a worker loader (worker-loader!./work.js), child_process.fork, a pino transport, and a module.register hook. An asset URL to a project module also goes to this group.

import type loads nothing, so fallow does not follow it. A declaration file (.d.ts) loads nothing, so it is not an entry and fallow does not count it when a value import reaches it. Test and config entry points are not runtime entries, so they are not in the report.

Startup import weight of 1 runtime entry (source bytes on disk, not bundle size)
src/main.ts (package.json main)
  eager          9 modules, 4.7 KiB (24 B CSS)
  deferred       4 modules, 209 B
  out of thread  3 modules, 141 B
  eager packages 2: lodash, react
  imports that keep the most bytes eager:
    src/main.ts:5 -> src/heavy/view.ts  3.7 KiB, 3 modules
    src/heavy/view.ts:1 -> src/heavy/chart-data.ts  2.7 KiB, 1 module

The unit is source bytes

Every byte count is the size of the source file on disk. Types, comments, and code that tree shaking removes all count, and bundler chunks do not apply. The value is therefore not a bundle size. It is a repeatable count: the same tree gives the same number, and the number goes down when you move an import behind import(). Package code is not measured: eager_packages lists the packages on the eager path with the specifiers as written, for example lodash/debounce. Platform built-ins such as node:fs are not listed.

Imports that keep the most bytes eager

dominating_imports lists single imports, heaviest first. For each import, exclusive_bytes is the number of bytes that leave the eager group if that one import becomes an import(). An import only counts when it is the only way into its target, so a module that two eager files import is never in the list. line is absent for an edge without a binding, such as export ... from or an eager glob match.

Treat this list as evidence for a review, not as a fix. A lazy load of code that the first screen needs can make startup slower. To see why one module is eager, run fallow trace --path <entry> <module> --eager-only. See Import path between two modules.

JSON output

{
  "entry_weight": {
    "unit": "source_bytes",
    "entry_count": 1,
    "entries": [
      {
        "path": "src/main.ts",
        "source": "package.json main",
        "eager_modules": 9,
        "eager_bytes": 4857,
        "eager_css_bytes": 24,
        "deferred_modules": 4,
        "deferred_bytes": 209,
        "out_of_thread_modules": 3,
        "out_of_thread_bytes": 141,
        "eager_package_count": 2,
        "eager_packages": [
          { "name": "lodash", "specifiers": ["lodash/debounce"] },
          { "name": "react", "specifiers": ["react"] }
        ],
        "dominating_imports": [
          {
            "importer": "src/main.ts",
            "line": 5,
            "target": "src/heavy/view.ts",
            "exclusive_bytes": 3825,
            "exclusive_modules": 3
          }
        ]
      }
    ]
  }
}

The report does not change the health score.

Gate the startup weight in CI

The gate is opt-in. Save a baseline file on your main branch:

fallow list --entry-weight --save-regression-baseline .fallow/regression-baseline.json

The file stores eager_bytes, eager_modules, and the eager package names of each entry. When the file already holds issue counts from fallow dead-code --save-regression-baseline, those counts stay, and the reverse is also true.

Compare a later run with the baseline:

# Report the change of each entry, exit 0
fallow list --entry-weight --regression-baseline .fallow/regression-baseline.json

# Exit 1 when an entry grew by more than 5%
fallow list --entry-weight --regression-baseline .fallow/regression-baseline.json \
  --fail-on-regression --tolerance 5%

The comparison adds entry_weight.regression to the JSON output. Each row has baseline_eager_bytes, current_eager_bytes, new_eager_packages, and exceeded. --tolerance takes a byte count, such as 1024, or a percentage, such as 5%. The default is 0. A new entry has nothing to grow from, so it never fails the gate. --save-regression-baseline needs a file path here, and a run with a [PATH] scope cannot save a baseline.

Examples

# Show active plugins
fallow list --plugins

# JSON output for scripting
fallow list --plugins --format json

Workspace diagnostics

Fallow reports workspace-discovery problems as workspace_diagnostics[] entries. The kind field tells you the type of problem:

kindWhen
malformed-package-jsonA declared workspace's package.json failed to parse. Includes the parse error.
glob-matched-no-package-jsonA workspace glob matched a directory that has no package.json (and is not in the conventional skip list or your ignorePatterns). Includes the matching pattern.
malformed-tsconfigtsconfig.json exists but failed to parse as JSONC. Includes the parse error.
tsconfig-reference-dir-missingtsconfig.json references a references[].path that does not exist.
malformed-pnpm-workspace-yamlpnpm-workspace.yaml exists but failed to parse as YAML. Fallow skips the catalog checks and ignores the file's override entries until you fix the syntax. pnpm install can accept a file that pnpm add and YAML formatters reject, for example a quoted value or a [...] list whose next line is not indented deeper than its key. Includes the parse error.
undeclared-workspaceA directory contains package.json but is not declared as a workspace.
excluded-by-default-ignoreA built-in discovery ignore pattern (**/dist/**, **/build/**, **/coverage/** or one of the minified-bundle globs) removed at least one candidate source file. Fallow reports one entry per pattern, never per file. The entry has the pattern, an exact file_count, and a directory_count of the distinct locations where the pattern matched. path is the project-relative matched directory that lost the most files. It is anchored at the deepest matching segment, so fallow --root <path> recovers the files. A !<path>/** exception in ignorePatterns brings the files back in the same run, and a lifted file is not counted. Fallow never reports **/node_modules/**. Source discovery emits this entry, so it appears in dead-code, check, audit and health JSON. --explain-skipped prints the same counts on stderr. A run that found no source files prints one warning without the flag.
bun-lockb-override-resolution-skippedThe root package.json declares overrides and has the legacy binary bun.lockb next to it. Fallow found no parseable text lockfile (bun.lock, pnpm-lock.yaml, package-lock.json, npm-shrinkwrap.json) to use instead. As a result, fallow skips the unused-dependency-overrides check, and the check reports nothing. A yarn.lock or an unparseable bun.lock next to the bun.lockb does not prevent the skip. To fix it, run bun install --save-text-lockfile (bun 1.2 or newer) to write a text bun.lock. If the repository no longer uses bun, delete the stale bun.lockb. The override analysis emits this entry, not workspace discovery. It appears in dead-code, check, and health JSON and as one stderr warning.
bun-lock-override-resolution-skippedThe root package.json declares overrides and the text bun.lock does not parse. Fallow found no other parseable lockfile (pnpm-lock.yaml, package-lock.json, npm-shrinkwrap.json) to use instead. As a result, fallow skips the unused-dependency-overrides check, and the check reports nothing. To fix it, run bun install to write the lockfile again. The override analysis emits this entry, not workspace discovery. It appears in dead-code, check, and health JSON and as one stderr warning.
pnpm-lock-override-resolution-skippedThe root declares overrides and pnpm-lock.yaml does not parse, for example because it still holds merge-conflict markers (<<<<<<<, =======, >>>>>>>). Fallow found no other parseable lockfile (package-lock.json, npm-shrinkwrap.json, bun.lock) to use instead. As a result, fallow skips the unused-dependency-overrides check, and the check reports nothing. To fix it, resolve the merge conflicts in the lockfile, or run pnpm install to write it again. The override analysis emits this entry, not workspace discovery. It appears in dead-code, check, and health JSON and as one stderr warning.
npm-lock-override-resolution-skippedThe root package.json declares overrides and package-lock.json or npm-shrinkwrap.json does not parse, for example because it still holds merge-conflict markers (<<<<<<<, =======, >>>>>>>). A lockfile that fallow cannot read, for example with bytes that are not UTF-8, counts as one that does not parse. Fallow found no other parseable lockfile (pnpm-lock.yaml, the other npm lockfile, bun.lock) to use instead. As a result, fallow skips the unused-dependency-overrides check, and the check reports nothing. The path is the lockfile that does not parse. To fix it, resolve the merge conflicts in the lockfile, or run npm install to write it again. The override analysis emits this entry, not workspace discovery. It appears in dead-code, check, and health JSON and as one stderr warning.

A malformed ROOT package.json is a hard config-load error, and fallow exits 2. For all other problems, fallow prints a warning and continues, so the analysis still runs on the remaining workspaces.

The JSON output of fallow dead-code, fallow dupes, and fallow health also contains these workspace-discovery diagnostics (omitted when empty). A CI job can fail on discovery problems without a separate command. The same JSON output can also include source-discovery skip diagnostics:

  • skipped-large-file and skipped-minified-file, each with size_bytes
  • excluded-by-default-ignore, for files that a built-in ignore pattern removed

Other kinds report problems with source reads and parsing, a missing node_modules directory, and checks that did not run. New kinds can appear in later releases. Treat an unknown kind as a new diagnostic, not as an error.

Example output

Boundaries: 6 zones, 6 rules, 1 logical group

Zones:
  app                  3 files  src/app/**
  features/auth        4 files  src/features/auth/**
  features/billing     5 files  src/features/billing/**
  features             3 files  src/features/**
  shared               8 files  src/components/**, src/hooks/**, src/lib/**, ...
  server               4 files  src/server/**

Rules:
  features/auth        → shared, server
  features/billing     → shared, server
  app                  → features/auth, features/billing, features, shared, server
  features             → shared, server, features/auth, features/billing
  server               → shared
  shared               (isolated, no imports allowed)

Logical groups:
  features             12 files autoDiscover: src/features
    children: features/auth, features/billing

The Logical groups: section appears when the config (or a preset) uses autoDiscover to split a parent zone into one zone per child directory. Each entry shows the parent name from your config, the exact autoDiscover paths as written, the discovered children, and the total file count.

In the human output, misconfigured entries (invalid path, empty) come first in the section. When the parent has both patterns and autoDiscover (for example, the Bulletproof preset), the file count shows as N files (X children + Y fallback).

The --format json output has the same data in a parallel boundaries.logical_groups[] array. Tools that write the config back can use these extra fields:

FieldMeaning
auto_discoverThe user strings, verbatim.
source_zone_indexThe position in the zones[] array before expansion.
statusok, empty, or invalid_path.
authored_ruleThe rule on the parent, before expansion.
fallback_zoneA back-reference when the parent kept its own patterns and stayed in zones[].
merged_fromThe parent zone indices when the config declares the same parent name twice.
original_zone_rootA copy of the root subtree scope of the parent.
child_source_indicesParallel to children. It links each child to one auto_discover entry.

docs/output-schema.json documents the full shape under ListBoundariesOutput.

Active plugins:
  - nextjs
  - vite
  - vitest
  - playwright
  - storybook
  - oxlint
  - typescript
  - tailwind
  - drizzle
  - commitlint
  - sentry
Found 42 entry points
src/app/layout.tsx                   (Plugin: nextjs)
src/app/page.tsx                     (Plugin: nextjs)
src/instrumentation.ts               (Plugin: nextjs)
src/proxy.ts                         (Plugin: nextjs)
src/lib/utils.test.ts                (Plugin: vitest)
e2e/auth.setup.ts                    (Plugin: playwright)
.storybook/main.ts                   (Plugin: storybook)
drizzle.config.ts                    (Plugin: drizzle)
scripts/db-migrate.ts                (PackageJsonScript)
scripts/db-seed.ts                   (PackageJsonScript)
...

Each entry point shows how fallow found it. The plugin name tells you which framework plugin detected it.

See also