Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,31 @@ The CLI will guide you through:
3. **Upload Preset** (Optional): handling unsigned uploads
4. **AI Assistant**: generating custom rules for your tool of choice (Cursor, VS Code, etc.)

## ⚙️ Headless Mode

For CI/CD pipelines, scripts, or automated workflows, pass `--headless` along with all options as flags to skip the interactive prompts:

```bash
npx create-cloudinary-react -- --headless \
--cloudName your-cloud-name \
--projectName my-app
```

**All options:**

| Flag | Type | Default | Description |
|---|---|---|---|
| `--cloudName` | string | *(required)* | Your Cloudinary cloud name |
| `--projectName` | string | `my-cloudinary-app` | Output directory name |
| `--hasUploadPreset` | boolean | `false` | Set if you have an unsigned upload preset |
| `--uploadPreset` | string | — | Your unsigned upload preset name (required if `--hasUploadPreset`) |
| `--aiTools` | string (repeatable) | `cursor` | AI tools to configure: `cursor`, `copilot`, `claude`, `generic` |
| `--installDeps` | boolean | `true` | Install dependencies after scaffolding |
| `--startDev` | boolean | `false` | Start the dev server after install |
| `--packageManager` | string | *(auto-detected)* | `npm`, `pnpm`, `yarn`, or `bun` |

> **Note:** If a flag value starts with a dash, use `--flag=value` syntax (e.g. `--cloudName=-myvalue`). Shell variables should be quoted: `--cloudName "$CLOUD_NAME"`.

## 🛠️ What's Included

Your new project comes with:
Expand Down
89 changes: 50 additions & 39 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,47 +88,58 @@ async function main() {

if (process.argv.includes('--headless')) {

const { values, positionals } = parseArgs({
options: {
headless: {
type: 'boolean'
},
projectName: {
type: 'string',
default: 'my-cloudinary-app'
},
cloudName: {
type: 'string'
},
hasUploadPreset: {
type: 'boolean',
default: false
},
uploadPreset: {
type: 'string'
},
aiTools: {
type: 'string',
multiple: true,
default: ['cursor']
},
installDeps: {
type: 'boolean',
default: true
},
startDev: {
type: 'boolean',
default: false
},
packageManager: {
type: 'string',
let values;
try {
({ values } = parseArgs({
args: process.argv.slice(2).filter(a => a !== '--'),
options: {
headless: { type: 'boolean' },
projectName: { type: 'string', default: 'my-cloudinary-app' },
cloudName: { type: 'string' },
hasUploadPreset: { type: 'boolean', default: false },
uploadPreset: { type: 'string' },
aiTools: { type: 'string', multiple: true, default: ['cursor'] },
installDeps: { type: 'boolean', default: true },
startDev: { type: 'boolean', default: false },
packageManager: { type: 'string' },
},
},
allowPositionals: true,
});

allowPositionals: true,
}));
} catch (e) {
console.error(chalk.red(`Error: ${e.message}`));
console.error(chalk.gray('Tip: If a flag value starts with a dash, use --flag=value syntax (e.g., --cloudName=-myvalue)'));
process.exit(1);
}

Object.assign(answers, values);


const errors = [];

if (!values.projectName) {
errors.push('--projectName is required');
} else if (!isValidProjectName(values.projectName)) {
errors.push('--projectName can only contain letters, numbers, hyphens, and underscores');
} else if (existsSync(values.projectName)) {
errors.push(`Directory "${values.projectName}" already exists. Please choose a different name.`);
}

if (!values.cloudName) {
errors.push('--cloudName is required');
} else if (!isValidCloudName(values.cloudName)) {
errors.push('--cloudName can only contain lowercase letters, numbers, hyphens, and underscores');
}

if (values.hasUploadPreset && !values.uploadPreset) {
errors.push('--uploadPreset is required when --hasUploadPreset is set');
}

if (errors.length > 0) {
for (const err of errors) {
console.error(chalk.red(`Error: ${err}`));
}
process.exit(1);
}

} else {

console.log(chalk.cyan.bold('\n🚀 Cloudinary React Starter Kit\n'));
Expand Down