zerostart-cli 0.0.21 → 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.21');
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();
@@ -522,7 +585,7 @@ program
522
585
  vSpinner.succeed(chalk_1.default.green('Authenticated with Vercel'));
523
586
  }
524
587
  const dSpinner = (0, ora_1.default)('Deploying to Vercel...').start();
525
- const deploymentUrl = await vercelManager.deploy(config.path);
588
+ const deploymentUrl = await vercelManager.deploy(config.path, config.name);
526
589
  if (deploymentUrl) {
527
590
  dSpinner.succeed(chalk_1.default.green('Deployed to Vercel!'));
528
591
  console.log(chalk_1.default.gray(' URL: ') + chalk_1.default.cyan(deploymentUrl));
@@ -592,6 +655,60 @@ program
592
655
  }
593
656
  const dSpinner = (0, ora_1.default)('Deploying to Netlify...').start();
594
657
  dSpinner.stop(); // Stop spinner to allow interactive input
658
+ // Try to create site first to enforce name
659
+ console.log();
660
+ console.log(chalk_1.default.cyan(` Configuring Netlify site for "${config.name}"...`));
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)
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
+ }
595
712
  const success = await netlifyManager.deploy(config.path);
596
713
  if (success) {
597
714
  console.log();
@@ -32,11 +32,15 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
35
38
  Object.defineProperty(exports, "__esModule", { value: true });
36
39
  exports.NetlifyManager = void 0;
37
40
  const cp = __importStar(require("child_process"));
38
41
  const util = __importStar(require("util"));
39
42
  const child_process_1 = require("child_process");
43
+ const chalk_1 = __importDefault(require("chalk"));
40
44
  const exec = util.promisify(cp.exec);
41
45
  class NetlifyManager {
42
46
  async checkNetlifyInstalled() {
@@ -58,6 +62,47 @@ class NetlifyManager {
58
62
  return false;
59
63
  }
60
64
  }
65
+ async createSite(name, cwd) {
66
+ try {
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.
70
+ return new Promise((resolve) => {
71
+ const child = (0, child_process_1.spawn)('netlify', ['sites:create', '--name', name], {
72
+ cwd,
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
83
+ });
84
+ child.on('close', (code) => {
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
+ }
98
+ });
99
+ });
100
+ }
101
+ catch (error) {
102
+ console.error('Failed to create Netlify site:', error);
103
+ return { success: false, reason: 'error' };
104
+ }
105
+ }
61
106
  async deploy(cwd) {
62
107
  return new Promise((resolve) => {
63
108
  // Use spawn with stdio: 'inherit' to allow interactive site creation
@@ -48,12 +48,13 @@ class VercelManager {
48
48
  return false;
49
49
  }
50
50
  }
51
- async deploy(cwd) {
51
+ async deploy(cwd, projectName) {
52
52
  try {
53
- // vercel deploy --prod --yes
54
- // --yes to skip confirmation
55
- // --prod to deploy to production
56
- const { stdout } = await exec('vercel deploy --prod --yes', { cwd });
53
+ // vercel deploy --prod --yes --name <name>
54
+ const cmd = projectName
55
+ ? `vercel deploy --prod --yes --name ${projectName}`
56
+ : 'vercel deploy --prod --yes';
57
+ const { stdout } = await exec(cmd, { cwd });
57
58
  return stdout.trim();
58
59
  }
59
60
  catch (error) {
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.21",
8
+ "version": "0.0.24",
9
9
  "engines": {
10
10
  "vscode": "^1.85.0"
11
11
  },