Skip to content

kekyo/dir4json

Repository files navigation

dir4json

A toolset for output directories/files structure to JSON

Project Status: WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public. License: MIT npm version


(Japanese is here/日本語はこちら)

What is this?

Even if you want to search for directories and files that serve as assets or resources at runtime, you cannot do so in environments like browsers. You're probably looking for a list of those directories and files.

dir4json is a Vite plugin and CLI toolset for TypeScript/JavaScript environments that makes this easy to achieve.

Using dir4json, this directory structure is generated as JSON during the build process:

public
├── images
│   ├── foo.jpg
│   └── bar.png
└── baz.mp3
{
  "public/": {   // Directory
    "images/": {   // Sub directory
      "foo.jpg": {   // File
        "size": 15344   // File size
      },
      "bar.png": {
        "size": 43791
      }
    },
    "baz.mp3": {
      "size": 115563
    }
  }
}

Features

  • Automatically emits a JSON file containing the files under the specified directories. You can import the "Metadata JSON," serve it from your web server, and reuse it in many ways.
  • As a Vite plugin, it generates metadata JSON during build time simply by being integrated. It can also be used as a CLI interface.
  • Files inside subdirectories are collected as well; directory hierarchy is preserved with keys that end in / so folders are distinct from files.
  • Each file entry records its size by default, and you can opt in to timestamps, MIME types, or hashes.
  • Optionally calculate hashes (Representative hash algorithms such as md5 and sha1) and embed them in the metadata.

Environment

  • NPM enviroment (TypeScript/JavaScript)
  • Vite 5.0 or higher.

Installation

Install it as a devDependency:

npm install -D dir4json

Or, to use it globally as a CLI:

npm install -g dir4json

Configure as a Vite Plugin

Enable the plugins in vite.config.ts:

// Import dir4json
import dir4json from 'dir4json';

export default defineConfig({
  plugins: [
    // Use dir4json plugin
    dir4json({
      dirs: [
        'public/images/',  // Directories to capture
        'src/asset/',
      ]
    }),
  ],
  // ...
});

Passing options is optional—when you only want to rely on .dir4jsonrc entries you can call the plugin as dir4json().

Specify the directory paths you want to export through the dirs option. When the build runs, the metadata JSON is written to src/generated/dirMetadata.json.

{
  "public/": {   // Directory (trailing slash marks folders)
    "images/": {   // Sub directory
      "foo.jpg": {   // File
        "size": 15344,   // File size in bytes
      },
      "bar.png": {
        "size": 43791,
      }
    },
    "baz.mp3": {
      "size": 115563,
    }
  },
  "src/": {
    "asset/": {
      "data/": {
        "data.db": {
          // ...
        }
      }
    }
  }
}

This allows you to easily access the directory structure:

// Import the JSON to use instantly:
import dirMetadata from './generated/dirMetadata.json';

// Look up a specific file
const barImage = dirMetadata['public/']['images/']['bar.png'];
console.log(`bar size: ${barImage.size} bytes`);

// Walk through a directory node
Object.entries(dirMetadata['src/']['asset/']).forEach(([name, entry]) => {
  console.log(`${name}: ${entry.size}`);
});

You could publish JSON on a web server and load it using fetch API.

Loading directories from .dir4jsonrc

dir4json automatically looks for .dir4jsonrc files under the project root (JSON5 syntax is accepted). In some cases, this is simpler than enumerating every directory through Vite plugin options.

export default defineConfig({
  plugins: [
    dir4json(),  // Rely entirely on .dir4jsonrc entries
  ],
  // ...
});

Place a .dir4jsonrc file inside the directory you want to scan:

{
  "dir": "*.jpg"
}

Each file can contain a single entry or an array that follows the dirs option shape—strings or objects with a dir property. Paths are interpreted relative to the .dir4jsonrc file, so writing the JSON above in content/docs/.dir4jsonrc scans content/docs/*.jpg.

Leaving the file empty falls back to tracking every file in the containing folder. All discovered entries are merged with the plugin configuration, and changes to any .dir4jsonrc trigger regeneration during development.

Configure as a Command Line Interface

dir4json also ships with a small CLI so you can regenerate metadata without Vite. The CLI treats the current working directory as the project root.

npx dir4json --output src/generated/images.json public/images/
  • The positional dir argument is optional; when omitted, entries are loaded from .dir4jsonrc.
  • --output, --attributes, --hashFormat, and --baseDir mirror the DirectoryEntry fields.
  • Attribute lists accept comma-separated values such as --attributes size,mtime,sha256.

Flexible configuration

dir4json lets you tailor the metadata output in several ways.

Directory selection

The dirs option accepts multiple paths, and each entry can be a glob pattern:

export default defineConfig({
  plugins: [
    dir4json({
      dirs: [
        'public/images/**/*.jpg',  // Include images recursively with a glob
        'src/asset/',
      ]
    }),
  ],
  // ...
});

Custom output targets

By default the metadata is written to src/generated/dirMetadata.json, but you can override the output path:

export default defineConfig({
  plugins: [
    dir4json({
      dirs: [
        {
          dir: 'public/images/',
          output: 'src/generated/images.json',  // Custom output
        },
        {
          dir: 'src/asset/data/**/*.db',
          output: 'src/generated/assets.json',  // Another output file
        },
      ],
    }),
  ],
});

All metadata is merged appropriately before being written to each output file.

Selecting output attributes

The metadata attributes written for each file are controlled by the attributes option. The default set is ['size'].

  • size, ctime, atime, mtime, mode, uid, and gid expose values returned by the filesystem stat call. Timestamp fields use the same timezone-aware ISO format as ctime, atime andmtime. size, mode, uid, and gid are emitted as numbers.
  • mime adds the MIME type inferred by the mime package (extension-based detection, so it may not always be perfect).
  • To embed hashes, append supported algorithm names to attributes. The available algorithms are md5, sha1, sha256, sha512, sha3-256, and sha3-512.

Example:

dir4json({
  dirs: [
    {
      dir: 'public/images/',
      attributes: ['size', 'mtime', 'mime', 'md5'],
    },
  ],
});

This adds the requested attribute values to each file entry:

{
  "public/": {
    "images/": {
      "foo.jpg": {
        "size": 15344,  // File size
        "mime": "image/jpeg",  // MIME type
        "mtime": "2021-12-23T08:12:47-05:00",  // Modified date
        "md5": "0cc175b9c0f1b6a831c399e269772661"  // md5 hash
      }
    }
  }
}

To emit multiple hashes per file, list all desired algorithms:

dir4json({
  dirs: [
    {
      dir: 'public/images/',
      attributes: ['size', 'mtime', 'mime', 'md5', 'sha1'],
    },
  ],
});
{
  "public/": {
    "images/": {
      "foo.jpg": {
        "size": 15344,
        "mime": "image/jpeg",
        "mtime": "2021-12-23T08:12:47-05:00",
        "md5": "0cc175b9c0f1b6a831c399e269772661",
        "sha1": "356a192b7913b04c54574d18c28d46e6395428ab"
      }
    }
  }
}

Hashes default to hexadecimal (hex). Switch to base64 with hashFormat when needed:

dir4json({
  dirs: [
    {
      dir: 'public/images/',
      attributes: ['size', 'mtime', 'mime', 'md5'],
      hashFormat: 'base64',
    },
  ],
});

Each dirs entry can still override its own attribute list and hash format:

dir4json({
  hashFormat: 'hex',  // Default hash format
  dirs: [
    {
      dir: 'private/documents/**/*.jpg',
      attributes: ['size', 'md5'],
    },
    {
      dir: 'public/images/',
      attributes: ['size', 'mtime', 'mime', 'sha256'],
      hashFormat: 'base64',  // Switch to base64 for this entry only
    },
  ],
});

Trimming base path segments

The generated JSON normally mirrors the directory structure that you pass to dirs.

If you want to skip the first portion of that path (for example, to drop a shared public/ folder), use the baseDir option. When the plugin-level baseDir is present, every entry resolves relative to it. Entry-level baseDir values are appended to the plugin-level path, so 'public' and 'docs' combine into 'public/docs'.

dir4json({
  baseDir: 'public',  // Origin directory is public (from the project root)
  dirs: [
    'images', // Scans public/images but the JSON starts at { "images/": { ... } }
    {
      baseDir: '../shared-content',  // Combined with public -> public/../shared-content
      dir: 'docs', // Scans shared-content/docs but the JSON starts at { "docs/": { ... } }
      output: 'src/generated/shared.json',
    },
  ],
});

Because baseDir values are resolved with Node's path.resolve, either component can be absolute. Absolute segments override the preceding ones automatically, so you can still target locations outside the project tree.

Each tree is still emitted relative to its own base path, so the metadata for /absolute/path/docs becomes { "docs/": { ... } } regardless of how far the directory is from the project root.


Discussions and Pull Requests

For discussions, please refer to the GitHub Discussions page. We have currently stopped issue-based discussions.

Pull requests are welcome! Please submit them as diffs against the develop branch and squashed changes before send.

License

Under MIT.

About

A toolset for output directories/files structure to JSON

Topics

Resources

License

Stars

Watchers

Forks