npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@oscaroca/drive-cms

v0.1.0

Published

Turn a Google Drive folder into a static JSON index — framework-agnostic headless CMS

Readme

drive-cms

Turn a Google Drive folder into a static JSON index — a framework-agnostic headless CMS with zero runtime dependencies.

How it works

You organize your content in a Google Drive folder. drive-cms scans the folder and writes a JSON file you can serve statically alongside your app. Each Google Doc (or HTML/PDF file) becomes one entry in the array.

Folder convention:

my-drive-folder/
├── My First Article          ← Google Doc → becomes an entry
├── My First Article.jpg      ← Same name → used as previewImage
└── Another Project           ← No image → previewImage is ""

Install

npm install -D drive-cms

Or use without installing:

npx drive-cms init
npx drive-cms sync

Setup

1. Scaffold a config file:

npx drive-cms init

This creates drive-cms.config.js in your project root.

2. Edit the config:

// drive-cms.config.js
import 'dotenv/config';

export default {
  apiKey: process.env.DRIVE_API_KEY,
  output: './public/data',
  collections: [
    {
      name: 'projects-en',
      folderId: process.env.DRIVE_FOLDER_ID_EN,
    },
    {
      name: 'projects-es',
      folderId: process.env.DRIVE_FOLDER_ID_ES,
    },
  ],
};

3. Make your Drive folder public:

Right-click the folder in Google Drive → Share → Anyone with the link → Viewer.

4. Run:

npx drive-cms sync

This writes public/data/projects-en.json and public/data/projects-es.json.

Getting a Google Drive API key

  1. Go to console.cloud.google.com
  2. Enable the Google Drive API
  3. Create an API Key under Credentials
  4. (Optional) Restrict the key to the Google Drive API

Tip: Create a separate unrestricted key for the CLI and a referrer-restricted key for your browser app.

Default output shape

Each entry in the JSON array has this shape by default:

{
  id: string;           // Google Drive file ID
  mimeType: string;     // e.g. "application/vnd.google-apps.document"
  title: string;        // File name (extension stripped)
  description: string;  // Drive file description field
  previewImage: string; // Drive ID of the paired image file (or "")
}

Set the description in Google Drive via right-click → File information → Details.

Custom output shape

Supply a transform function to control exactly what goes into the JSON:

export default {
  apiKey: process.env.DRIVE_API_KEY,
  output: './public/data',
  collections: [{ name: 'posts', folderId: process.env.FOLDER_ID }],
  transform: (doc, image) => ({
    slug: doc.name.toLowerCase().replace(/\s+/g, '-').replace(/\.[^.]+$/, ''),
    title: doc.name.replace(/\.[^.]+$/, ''),
    summary: doc.description ?? '',
    cover: image ? `https://lh3.googleusercontent.com/d/${image.id}=w800` : null,
    driveId: doc.id,
  }),
};

The transform function receives:

  • doc — the Google Drive file object ({ id, name, mimeType, description, thumbnailLink })
  • image — the paired image file, or null if none was found

CLI reference

drive-cms sync                          # sync all collections
drive-cms sync --collection posts       # sync a single collection by name
drive-cms init                          # scaffold drive-cms.config.js

Programmatic usage

import { loadConfig, syncAll } from 'drive-cms';

const config = await loadConfig();
const results = await syncAll(config);

for (const result of results) {
  console.log(`${result.collection}: ${result.entryCount} entries → ${result.outputPath}`);
}

Config reference

| Field | Type | Required | Default | |---|---|---|---| | apiKey | string | Yes | — | | output | string | No | "./public/data" | | collections | Collection[] | Yes | — | | transform | (doc, image) => T | No | Default shape above |

Collection:

| Field | Type | Required | |---|---|---| | name | string | Yes — used as output filename | | folderId | string | Yes — Google Drive folder ID |

License

MIT