フォルダ構造を出力してプロジェクト全体を把握しよう!


こんにちは。フロントエンドエンジニアの辻です。

プロダクトチームの支援をしている Gaji-Labo は、プロジェクトが終了したあとのことも考えて、ドキュメント整備も実装と同じくらい力を入れています。


さて、ドキュメントを作成する時に、フォルダ(ディレクトリ)構造を出力したいときがあると思います。
例えば、次のようなツリー構造ですね。

./
├── docs
├── eslint.config.js
├── index.html
├── package.json
├── public
│   └── vite.svg
├── README.md
├── src
│   ├── App.css
│   ├── App.tsx
│   ├── assets … 画像などを格納する
│   │   ├── images
│   │   └── pdfs
│   ├── components … コンポーネントファイルを格納する
│   │   ├── atoms
│   │   ├── molecules
│   │   ├── organisms
│   │   ├── pages
│   │   └── templates
│   ├── hooks … カスタムフックを格納する
│   ├── libs … 外部ライブラリ関連の処理を格納する
│   ├── mocks … モックファイルを格納する
│   ├── stores … グローバルストア関連を格納する
│   ├── styles … CSS を格納する
│   ├── utils … 汎用的に処理を格納する … etc
│   ├── index.css
│   ├── main.tsx
│   └── vite-env.d.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

こういったフォルダ構造がドキュメントに含まれていると、プロジェクト全体がどのような構造かをおおよで把握できるので、大変助かります。

今回は Mac と Windows のそれぞれで、フォルダ構造を出力する方法を見ていきます。

Mac では tree コマンドが便利

Mac でのフォルダ構造の出力ツールは様々あると思いますが、簡単かつ便利に使えるモノとしては tree コマンドが筆頭に挙がるでしょう。かく言う筆者も愛用しています。

Mac で tree コマンドを利用するには、別途 インストールする必要があります。
Homebrew を使えば brew install tree で、すぐにインストールできます。
> tree — Homebrew Formulae


例として、Next.js v15 のプロジェクトを作成して tree -I node_modules コマンドを実行してみます。
-I は、特定のフォルダを出力しないためのオプションです。
tree だけで実行してしまうと、node_modules 配下も対象となり、メチャメチャ長文の出力結果になってしまいます。)

実行してみると、次の結果が出力されます。
あとは得られた出力結果に補足文を加えて、ドキュメントに書いておけば OK ですね。

.
├── app
│   ├── favicon.ico
│   ├── globals.css
│   ├── layout.tsx
│   └── page.tsx
├── eslint.config.mjs
├── next-env.d.ts
├── next.config.ts
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── public
│   ├── file.svg
│   ├── globe.svg
│   ├── next.svg
│   ├── vercel.svg
│   └── window.svg
├── README.md
└── tsconfig.json

ちなみに tree --help を実行すると、-I の他の様々なオプションを教えてくれます。
個人的には、不可視ファイルを表示する -a オプションや、特定の条件にマッチするフォルダ・ファイルのみを出力する -P オプションを、よく利用しますね。

Windows でフォルダ構造を出力したいとき

Windows では tree コマンドが標準搭載されています。
パッとフォルダ構造を出力したいときは、コマンドプロンプトで tree を実行するだけで事足ります。

しかしながら、Windows の tree コマンドには必要最小限のオプションのみが用意されており、先程の -I のような便利なオプションがありません。


では、Windows においてフォルダ構造任意でカスタマイズして出力する方法は…

調査してみたものの、うまい方法が見当たりませんでした…。
当方、Mac ユーザーのため、Windows があまり詳しくなく…。


仕方ないので、今回は AI と一緒に「フォルダ構造を出力する」スクリプトを JavaScript で作成してみました。
Windows に Node.js がインストールされていれば、node ファイル名.js で実行できます。

ちなみに、node_modules と .git と .next は、デフォルトで出力の対象外にしています。
node ファイル.js --exclude node_modules,.git,.next,build,dist とすれば、build や dist を除外することもできます。

実際のプロジェクトで利用するためのデフォルト設定でしたので、よしなに変更してください。

import fs from "fs";
import path from "path";

/**
 * フォルダ構造をツリー形式で表示する関数。
 *
 * @param {string} dir - ルートとなるフォルダのパス。デフォルトはカレントフォルダ。
 * @param {string} prefix - ツリー表示用の接頭辞。
 * @param {string[]} excludeDirs - 除外対象のフォルダ名。
 * @returns {void}
 */
const showTree = (
  dir = ".",
  prefix = "",
  excludeDirs = ["node_modules", ".git", ".next"]
) => {
  try {
    const items = fs
      .readdirSync(dir, { withFileTypes: true })
      .filter((item) => !excludeDirs.includes(item.name))
      .sort((a, b) => {
        // フォルダを先に配置する
        // その後、ファイルをアルファベット順にソートする
        if (a.isDirectory() && !b.isDirectory()) return -1;
        if (!a.isDirectory() && b.isDirectory()) return 1;
        return a.name.localeCompare(b.name);
      });

    // パス記号を接頭辞として付与
    items.forEach((item, index) => {
      const isLast = index === items.length - 1;
      const currentPrefix = isLast ? "└── " : "├── ";
      const nextPrefix = isLast ? "    " : "│   ";

      console.log(prefix + currentPrefix + item.name);

      // フォルダの場合は再帰的に実行する
      if (item.isDirectory()) {
        const fullPath = path.join(dir, item.name);
        showTree(fullPath, prefix + nextPrefix, excludeDirs);
      }
    });
  } catch (error) {
    console.error(`Error reading directory ${dir}:`, error.message);
  }
};

/**
 * コマンドライン引数を解析する関数。
 *
 * @returns {{ targetDir: string, excludeDirs: string[] } | null} - 対象フォルダと除外フォルダの設定を含むオブジェクト。ヘルプ表示時は null を返す。
 */
const parseArgs = () => {
  const args = process.argv.slice(2);
  let targetDir = ".";
  let excludeDirs = ["node_modules", ".git", ".next"];

  for (let i = 0; i < args.length; i++) {
    const arg = args[i];

    if (arg === "--exclude" || arg === "-e") {
      // 次の引数を除外フォルダとして取得
      if (i + 1 < args.length) {
        excludeDirs = args[i + 1].split(",").map((dir) => dir.trim());
        i++; // 次の引数をスキップ
      }
    } else if (arg === "--help" || arg === "-h") {
      console.log(`使用方法: node tree.js [フォルダ] [オプション]

オプション:
  --exclude, -e <dirs>  除外するフォルダをカンマ区切りで指定
  --help, -h           このヘルプを表示

例:
  node ファイル.js                                    # カレントフォルダを表示
  node ファイル.js ./src                              # srcフォルダを表示
  node ファイル.js --exclude node_modules,dist        # node_modulesとdistを除外
  node ファイル.js ./src --exclude .git,build         # srcフォルダから.gitとbuildを除外`);
      return null;
    } else if (!arg.startsWith("-")) {
      // オプションでない場合はフォルダとして扱う
      targetDir = arg;
    }
  }

  return { targetDir, excludeDirs };
};

// 実行
const config = parseArgs();
if (config) {
  console.log(`${config.targetDir}`);
  console.log(`除外フォルダ: ${config.excludeDirs.join(", ")}`);
  console.log("");
  showTree(config.targetDir, "", config.excludeDirs);
}

まとめ

以上、「フォルダ構造を出力してプロジェクト全体を把握しよう!」でした。

Windows におけるフォルダ構造の出力が尻切れトンボになってしまいましたね。
また、機会がありましたら、Windows におけるフォルダ構造の出力方法をちゃんと調べてみたいと思います。
(PowerShell やら grep やらをグリングリンするといけるのでしょうかね??)


Gaji-Labo では、実装と同じくらいプロジェクトのドキュメント整備にも力を入れています。
実装力だけが Gaji-Labo の強みではありません。
最終的にプロジェクトから Gaji-Labo が離れても、破綻せずに持続できるようドキュメント整備も大切な仕事と捉えています。

新規開発だけでなく、プロジェクトのリファクタリングについても、ぜひご相談ください!

Gaji-Labo は HTML, CSS 開発の実績と知見があります

複雑化した CSS や、HTML ファイルの階層…。
いつかリファクタリングをしなくては…と考えながら負債がたまり続け、開発効率が落ちている。
そんな状況のプロダクトや開発チームのお手伝いを得意としています。

フロントエンド開発の専門企業である Gaji-Labo は、複雑化した HTML・CSS のリファクタリングプロジェクトの課題整理・開発スコープやプロジェクトの定義から、リファクタリング実務まで対応が可能です。

HTML や CSS のリファクタリングの相談をする!

Gaji-Labo Culture Deck

Gaji-Labo は新規事業やサービス開発に取り組む、事業会社・スタートアップへの支援を行っています。

弊社では、Next.js を用いた Web アプリケーションのフロントエンド開発をリードするフロントエンドエンジニアを募集しています!さまざまなプロダクトやチームに関わりながら、一緒に成長を体験しませんか?

もちろん、一緒にお仕事をしてくださるパートナーさんも随時募集中です。まずはお気軽に声をかけてください!

求人応募してみる!


投稿者 Tsuji Atsuhiro

フロントエンドエンジニア。 DTP・Webデザイナーを経験した後、フロントエンドエンジニアに転向。HTML/CSS/JavaScriptを中心にWeb開発を担当してきました。 UI・UXに興味があり、デザイン・コーディング両面から考えられるデザインエンジニアを目指しています。 普段はマラソンやボクシングなどで体を動かしてます。