# How to Recursively List Files in GeoLibre: 3 Methods for the Monorepo

> Recursively list files in GeoLibre using git ls-files, find, or a custom Node.js walker. Optimize your monorepo file management with these three effective methods.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: how-to-guide
- Published: 2026-08-18

---

**To recursively list files in GeoLibre, use `git ls-files` for version-controlled files, `find` for directory traversal, or a custom Node.js walker for formatted tree output.**

GeoLibre is a large monorepo maintained by opengeos that spans TypeScript/JavaScript workspaces, a Python backend, Rust components, and extensive test suites. Whether you need a quick file inventory or a hierarchical tree view, these three approaches cover every scenario.

## Method 1: Git ls-files for Tracked Files

The fastest way to enumerate every version-controlled file uses Git's built-in plumbing command:

```bash
git ls-files

```

This scans the entire repository index—covering `apps/`, `packages/`, `workers/`, `backend/`, `python/`, and all other workspaces—without touching the working directory. For filenames containing spaces, add the `-z` flag for NUL-separated output safe for scripting:

```bash
git ls-files -z

```

**Why this works for GeoLibre:** The [`package.json`](https://github.com/opengeos/GeoLibre/blob/main/package.json) at the repository root defines workspace globs (`apps/*`, `packages/*`, `workers/*`), but Git's index already contains every tracked path regardless of workspace boundaries.

## Method 2: Find Command for Directory Trees

When you need a tree-like visualization without dependencies, combine `find` with `sed`:

```bash
find . -type f | sed 's|[^/]*/|│   |g; s|│   \([^│]*\)$|├── \1|'

```

This produces a hierarchical display by manipulating path separators. The command works on any fresh clone without requiring Git history access.

**Limitation:** This includes untracked files and respects `.gitignore` only if combined with Git. For a cleaner list, pipe through `git check-ignore --stdin` to filter ignored paths.

## Method 3: Node.js Recursive Walker

For programmatic access with formatted output, use this [`scripts/list-files.js`](https://github.com/opengeos/GeoLibre/blob/main/scripts/list-files.js) implementation that leverages Node.js `fs/promises`:

```javascript
// file: scripts/list-files.js
import { readdir, stat } from 'node:fs/promises';
import { join } from 'node:path';

async function walk(dir, prefix = '') {
  const entries = await readdir(dir, { withFileTypes: true });
  for (const [i, entry] of entries.entries()) {
    const isLast = i === entries.length - 1;
    const connector = isLast ? '└─ ' : '├─ ';
    console.log(prefix + connector + entry.name);
    if (entry.isDirectory()) {
      const newPrefix = prefix + (isLast ? '   ' : '│  ');
      await walk(join(dir, entry.name), newPrefix);
    }
  }
}

await walk(process.cwd());

```

Execute from the repository root:

```bash
node scripts/list-files.js

```

**When to use this:** Build scripts, CI pipelines, or custom tooling that needs to process files while displaying progress. The `withFileTypes: true` option distinguishes files from directories without additional `stat` calls.

## Key Files to Know in GeoLibre

These paths represent the repository's structural boundaries—each appears in recursive file listings:

| File | Role in File Enumeration |
|------|--------------------------|
| [`README.md`](https://github.com/opengeos/GeoLibre/blob/main/README.md) | Documents high-level layout and entry points |
| [`package.json`](https://github.com/opengeos/GeoLibre/blob/main/package.json) | Defines workspace roots that expand the file tree |
| [`backend/geolibre_server/pyproject.toml`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/pyproject.toml) | Python side-car project, tracked alongside JS workspaces |
| [`apps/geolibre-desktop/src-tauri/Cargo.toml`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src-tauri/Cargo.toml) | Rust component demonstrating cross-language monorepo structure |
| `tests/` | Extensive test suite recursively included in all listing methods |

## Method Comparison

| Approach | Speed | Includes Untracked | Tree Output | Best For |
|----------|-------|-------------------|-------------|----------|
| `git ls-files` | Fastest | No | No | CI manifests, build scripts |
| `find` + `sed` | Fast | Yes | Yes | Quick terminal visualization |
| Node.js walker | Moderate | Configurable | Yes | Custom tooling, filtering |

## Summary

- **`git ls-files`** — fastest for version-controlled inventory across GeoLibre's monorepo workspaces
- **`find . -type f`** — portable directory traversal with optional formatting via `sed`
- **Custom Node.js walker** — programmatic control for scripts requiring `readdir` with `withFileTypes`

All three correctly traverse the mixed-language structure spanning TypeScript workspaces in `apps/` and `packages/`, the Python backend in `backend/geolibre_server/`, and Rust components like `apps/geolibre-desktop/src-tauri/`.

## Frequently Asked Questions

### Does `git ls-files` include files in all GeoLibre workspaces?

Yes. `git ls-files` reads the Git index directly, so it captures every tracked file regardless of workspace configuration in [`package.json`](https://github.com/opengeos/GeoLibre/blob/main/package.json). This includes `apps/*`, `packages/*`, `workers/*`, and the Python backend.

### How do I exclude node_modules from recursive file listings?

`git ls-files` automatically excludes `node_modules` because directories listed in `.gitignore` are untracked. For `find`, add `-not -path '*/node_modules/*'` before the `| sed` pipe.

### Can I filter files by extension when listing recursively?

With `git ls-files`, append a pathspec: `git ls-files '*.ts' '*.py'`. For the Node.js walker, add an `if (!entry.name.endsWith('.ts')) continue` check before the `console.log` call.

### What's the performance difference between这些方法?

`git ls-files` operates on the index in milliseconds even for repositories with 10,000+ files. `find` scales linearly with disk I/O. The Node.js walker adds Promise overhead but remains efficient for typical GeoLibre sizes (under one second for the full tree).