zerostart-cli 0.0.22 → 0.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/out/cli.js CHANGED
@@ -83,7 +83,7 @@ function showGitHubTokenHelp() {
83
83
  program
84
84
  .name('zerostart')
85
85
  .description('Create and deploy a complete project with one command')
86
- .version('0.0.22');
86
+ .version('0.0.24');
87
87
  program
88
88
  .command('deploy-vercel')
89
89
  .description('Deploy the current project to Vercel')
@@ -142,8 +142,21 @@ program
142
142
  else {
143
143
  vSpinner.succeed(chalk_1.default.green('Authenticated'));
144
144
  }
145
+ // Prompt for project name for Vercel
146
+ const defaultName = path.basename(cwd);
147
+ const { vName } = await inquirer_1.default.prompt([{
148
+ type: 'input',
149
+ name: 'vName',
150
+ message: 'Vercel Project Name:',
151
+ default: defaultName,
152
+ validate: (input) => {
153
+ if (input.trim() === '')
154
+ return 'Name is required';
155
+ return true;
156
+ }
157
+ }]);
145
158
  const dSpinner = (0, ora_1.default)('Deploying to Vercel...').start();
146
- const url = await vercelManager.deploy(cwd);
159
+ const url = await vercelManager.deploy(cwd, vName);
147
160
  if (url) {
148
161
  dSpinner.succeed(chalk_1.default.green('Deployed to Vercel!'));
149
162
  console.log(chalk_1.default.gray(' URL: ') + chalk_1.default.cyan(url));
@@ -212,8 +225,58 @@ program
212
225
  else {
213
226
  nSpinner.succeed(chalk_1.default.green('Authenticated'));
214
227
  }
228
+ // Enforce name for standalone deploy
229
+ const defaultName = path.basename(cwd);
230
+ let siteName = defaultName;
231
+ let siteCreated = false;
232
+ while (!siteCreated) {
233
+ const result = await netlifyManager.createSite(siteName, cwd);
234
+ if (result.success) {
235
+ siteCreated = true;
236
+ }
237
+ else if (result.reason === 'taken') {
238
+ console.log(chalk_1.default.yellow(`\n The site name "${siteName}" is already taken.`));
239
+ const { newName } = await inquirer_1.default.prompt([{
240
+ type: 'input',
241
+ name: 'newName',
242
+ message: 'Enter a unique name for your Netlify site:',
243
+ validate: (input) => {
244
+ if (input.trim() === '')
245
+ return 'Name is required';
246
+ if (!/^[a-zA-Z0-9-_]+$/.test(input))
247
+ return 'Use only letters, numbers, hyphens, and underscores';
248
+ return true;
249
+ }
250
+ }]);
251
+ siteName = newName;
252
+ }
253
+ else {
254
+ console.log(chalk_1.default.red('\n Failed to configure Netlify site.'));
255
+ const { action } = await inquirer_1.default.prompt([{
256
+ type: 'list',
257
+ name: 'action',
258
+ message: 'What would you like to do?',
259
+ choices: ['Try again with a different name', 'Continue deployment anyway', 'Abort']
260
+ }]);
261
+ if (action === 'Abort')
262
+ return;
263
+ if (action === 'Continue deployment anyway')
264
+ break;
265
+ const { retryName } = await inquirer_1.default.prompt([{
266
+ type: 'input',
267
+ name: 'retryName',
268
+ message: 'Enter a unique name:',
269
+ validate: (input) => {
270
+ if (input.trim() === '')
271
+ return 'Name is required';
272
+ return true;
273
+ }
274
+ }]);
275
+ siteName = retryName;
276
+ }
277
+ }
215
278
  const dSpinner = (0, ora_1.default)('Deploying to Netlify...').start();
216
- dSpinner.stop(); // Stop spinner to allow interactive input
279
+ dSpinner.stop();
217
280
  const success = await netlifyManager.deploy(cwd);
218
281
  if (success) {
219
282
  console.log();
@@ -596,7 +659,56 @@ program
596
659
  console.log();
597
660
  console.log(chalk_1.default.cyan(` Configuring Netlify site for "${config.name}"...`));
598
661
  // We do this interactively so if name is taken user can see error and handle it (or it just links if they own it)
599
- await netlifyManager.createSite(config.name, config.path);
662
+ let siteName = config.name;
663
+ let siteCreated = false;
664
+ while (!siteCreated) {
665
+ const result = await netlifyManager.createSite(siteName, config.path);
666
+ if (result.success) {
667
+ siteCreated = true;
668
+ }
669
+ else if (result.reason === 'taken') {
670
+ console.log(chalk_1.default.yellow(`\n The site name "${siteName}" is already taken.`));
671
+ const { newName } = await inquirer_1.default.prompt([{
672
+ type: 'input',
673
+ name: 'newName',
674
+ message: 'Enter a unique name for your Netlify site:',
675
+ validate: (input) => {
676
+ if (input.trim() === '')
677
+ return 'Name is required';
678
+ if (!/^[a-zA-Z0-9-_]+$/.test(input))
679
+ return 'Use only letters, numbers, hyphens, and underscores';
680
+ return true;
681
+ }
682
+ }]);
683
+ siteName = newName;
684
+ }
685
+ else {
686
+ console.log(chalk_1.default.red('\n Failed to create Netlify site. '));
687
+ // Allow user to try to deploy anyway (maybe manual link?) or abort
688
+ const { action } = await inquirer_1.default.prompt([{
689
+ type: 'list',
690
+ name: 'action',
691
+ message: 'What would you like to do?',
692
+ choices: ['Try again with a different name', 'Continue deployment (might fail or link to wrong site)', 'Abort']
693
+ }]);
694
+ if (action === 'Abort')
695
+ return;
696
+ if (action === 'Continue deployment (might fail or link to wrong site)')
697
+ break;
698
+ // If try again, ask for name
699
+ const { retryName } = await inquirer_1.default.prompt([{
700
+ type: 'input',
701
+ name: 'retryName',
702
+ message: 'Enter a unique name:',
703
+ validate: (input) => {
704
+ if (input.trim() === '')
705
+ return 'Name is required';
706
+ return true;
707
+ }
708
+ }]);
709
+ siteName = retryName;
710
+ }
711
+ }
600
712
  const success = await netlifyManager.deploy(config.path);
601
713
  if (success) {
602
714
  console.log();
@@ -65,20 +65,42 @@ class NetlifyManager {
65
65
  async createSite(name, cwd) {
66
66
  try {
67
67
  console.log(chalk_1.default.cyan(` Creating Netlify site "${name}"...`));
68
+ // We need to capture output to detect "already exists" error
69
+ // sites:create fails if name is taken.
68
70
  return new Promise((resolve) => {
69
- // Using 'sites:create' might prompt if not authenticated, but we check auth before this.
70
71
  const child = (0, child_process_1.spawn)('netlify', ['sites:create', '--name', name], {
71
72
  cwd,
72
- stdio: 'inherit'
73
+ stdio: ['inherit', 'pipe', 'pipe'] // Pipe stdout/stderr to capture
74
+ });
75
+ let output = '';
76
+ child.stdout?.on('data', (data) => {
77
+ output += data.toString();
78
+ process.stdout.write(data); // Passthrough
79
+ });
80
+ child.stderr?.on('data', (data) => {
81
+ output += data.toString();
82
+ process.stderr.write(data); // Passthrough
73
83
  });
74
84
  child.on('close', (code) => {
75
- resolve(code === 0);
85
+ if (code === 0) {
86
+ resolve({ success: true });
87
+ }
88
+ else {
89
+ // Check for common "name taken" indicators in Netlify CLI output
90
+ // "Already exists" or "name is already taken"
91
+ if (output.toLowerCase().includes('already exists') || output.toLowerCase().includes('taken')) {
92
+ resolve({ success: false, reason: 'taken' });
93
+ }
94
+ else {
95
+ resolve({ success: false, reason: 'error' });
96
+ }
97
+ }
76
98
  });
77
99
  });
78
100
  }
79
101
  catch (error) {
80
102
  console.error('Failed to create Netlify site:', error);
81
- return false;
103
+ return { success: false, reason: 'error' };
82
104
  }
83
105
  }
84
106
  async deploy(cwd) {
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "zerostart": "./out/cli.js"
6
6
  },
7
7
  "description": "Create and deploy a complete project with one command.",
8
- "version": "0.0.22",
8
+ "version": "0.0.24",
9
9
  "engines": {
10
10
  "vscode": "^1.85.0"
11
11
  },