npm package: @oscaroca/drive-cms
Description: Turn a Google Drive folder into a static JSON index — framework-agnostic headless CMS
Author: Oscar Oca
Latest version: 0.1.0

# 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

```bash
npm install -D drive-cms
```

Or use without installing:

```bash
npx drive-cms init
npx drive-cms sync
```

## Setup

**1. Scaffold a config file:**

```bash
npx drive-cms init
```

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

**2. Edit the config:**

```js
// 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:**

```bash
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](https://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:

```ts
{
  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:

```js
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

```bash
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

```ts
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