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 \
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need the --

--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"`.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto for my above note. Feels...rare.


## 🛠️ 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)'));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are cloud names or upload preset names allowed to start with a dash? At the very least, this feels kind of rare to show every time anyone encounters any error

process.exit(1);
}

Object.assign(answers, values);


const errors = [];

if (!values.projectName) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not actually required, because of the default of my-cloudinary-app (we should still keep the "does the directory already exist" check though!)

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