Skip to content

Commit 64ed089

Browse files
author
strausr
committed
fix: add headless mode validation and documentation
1 parent 7f6db64 commit 64ed089

2 files changed

Lines changed: 75 additions & 39 deletions

File tree

‎README.md‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,31 @@ The CLI will guide you through:
4949
3. **Upload Preset** (Optional): handling unsigned uploads
5050
4. **AI Assistant**: generating custom rules for your tool of choice (Cursor, VS Code, etc.)
5151

52+
## ⚙️ Headless Mode
53+
54+
For CI/CD pipelines, scripts, or automated workflows, pass `--headless` along with all options as flags to skip the interactive prompts:
55+
56+
```bash
57+
npx create-cloudinary-react -- --headless \
58+
--cloudName your-cloud-name \
59+
--projectName my-app
60+
```
61+
62+
**All options:**
63+
64+
| Flag | Type | Default | Description |
65+
|---|---|---|---|
66+
| `--cloudName` | string | *(required)* | Your Cloudinary cloud name |
67+
| `--projectName` | string | `my-cloudinary-app` | Output directory name |
68+
| `--hasUploadPreset` | boolean | `false` | Set if you have an unsigned upload preset |
69+
| `--uploadPreset` | string | — | Your unsigned upload preset name (required if `--hasUploadPreset`) |
70+
| `--aiTools` | string (repeatable) | `cursor` | AI tools to configure: `cursor`, `copilot`, `claude`, `generic` |
71+
| `--installDeps` | boolean | `true` | Install dependencies after scaffolding |
72+
| `--startDev` | boolean | `false` | Start the dev server after install |
73+
| `--packageManager` | string | *(auto-detected)* | `npm`, `pnpm`, `yarn`, or `bun` |
74+
75+
> **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"`.
76+
5277
## 🛠️ What's Included
5378

5479
Your new project comes with:

‎cli.js‎

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -88,47 +88,58 @@ async function main() {
8888

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

91-
const { values, positionals } = parseArgs({
92-
options: {
93-
headless: {
94-
type: 'boolean'
95-
},
96-
projectName: {
97-
type: 'string',
98-
default: 'my-cloudinary-app'
99-
},
100-
cloudName: {
101-
type: 'string'
102-
},
103-
hasUploadPreset: {
104-
type: 'boolean',
105-
default: false
106-
},
107-
uploadPreset: {
108-
type: 'string'
109-
},
110-
aiTools: {
111-
type: 'string',
112-
multiple: true,
113-
default: ['cursor']
114-
},
115-
installDeps: {
116-
type: 'boolean',
117-
default: true
118-
},
119-
startDev: {
120-
type: 'boolean',
121-
default: false
122-
},
123-
packageManager: {
124-
type: 'string',
91+
let values;
92+
try {
93+
({ values } = parseArgs({
94+
args: process.argv.slice(2).filter(a => a !== '--'),
95+
options: {
96+
headless: { type: 'boolean' },
97+
projectName: { type: 'string', default: 'my-cloudinary-app' },
98+
cloudName: { type: 'string' },
99+
hasUploadPreset: { type: 'boolean', default: false },
100+
uploadPreset: { type: 'string' },
101+
aiTools: { type: 'string', multiple: true, default: ['cursor'] },
102+
installDeps: { type: 'boolean', default: true },
103+
startDev: { type: 'boolean', default: false },
104+
packageManager: { type: 'string' },
125105
},
126-
},
127-
allowPositionals: true,
128-
});
129-
106+
allowPositionals: true,
107+
}));
108+
} catch (e) {
109+
console.error(chalk.red(`Error: ${e.message}`));
110+
console.error(chalk.gray('Tip: If a flag value starts with a dash, use --flag=value syntax (e.g., --cloudName=-myvalue)'));
111+
process.exit(1);
112+
}
113+
130114
Object.assign(answers, values);
131-
115+
116+
const errors = [];
117+
118+
if (!values.projectName) {
119+
errors.push('--projectName is required');
120+
} else if (!isValidProjectName(values.projectName)) {
121+
errors.push('--projectName can only contain letters, numbers, hyphens, and underscores');
122+
} else if (existsSync(values.projectName)) {
123+
errors.push(`Directory "${values.projectName}" already exists. Please choose a different name.`);
124+
}
125+
126+
if (!values.cloudName) {
127+
errors.push('--cloudName is required');
128+
} else if (!isValidCloudName(values.cloudName)) {
129+
errors.push('--cloudName can only contain lowercase letters, numbers, hyphens, and underscores');
130+
}
131+
132+
if (values.hasUploadPreset && !values.uploadPreset) {
133+
errors.push('--uploadPreset is required when --hasUploadPreset is set');
134+
}
135+
136+
if (errors.length > 0) {
137+
for (const err of errors) {
138+
console.error(chalk.red(`Error: ${err}`));
139+
}
140+
process.exit(1);
141+
}
142+
132143
} else {
133144

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

0 commit comments

Comments
 (0)