zerostart-cli 0.0.24 → 0.0.26
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 +6 -130
- package/out/managers/NetlifyManager.js +16 -12
- package/out/managers/TemplateManager.js +56 -0
- package/out/types.js +1 -0
- package/package.json +1 -1
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.
|
|
86
|
+
.version('0.0.26');
|
|
87
87
|
program
|
|
88
88
|
.command('deploy-vercel')
|
|
89
89
|
.description('Deploy the current project to Vercel')
|
|
@@ -229,10 +229,12 @@ program
|
|
|
229
229
|
const defaultName = path.basename(cwd);
|
|
230
230
|
let siteName = defaultName;
|
|
231
231
|
let siteCreated = false;
|
|
232
|
+
let activeSiteId = undefined;
|
|
232
233
|
while (!siteCreated) {
|
|
233
234
|
const result = await netlifyManager.createSite(siteName, cwd);
|
|
234
235
|
if (result.success) {
|
|
235
236
|
siteCreated = true;
|
|
237
|
+
activeSiteId = result.siteId;
|
|
236
238
|
}
|
|
237
239
|
else if (result.reason === 'taken') {
|
|
238
240
|
console.log(chalk_1.default.yellow(`\n The site name "${siteName}" is already taken.`));
|
|
@@ -277,7 +279,7 @@ program
|
|
|
277
279
|
}
|
|
278
280
|
const dSpinner = (0, ora_1.default)('Deploying to Netlify...').start();
|
|
279
281
|
dSpinner.stop();
|
|
280
|
-
const success = await netlifyManager.deploy(cwd);
|
|
282
|
+
const success = await netlifyManager.deploy(cwd, activeSiteId);
|
|
281
283
|
if (success) {
|
|
282
284
|
console.log();
|
|
283
285
|
console.log(chalk_1.default.green(' ✔ Deployed to Netlify!'));
|
|
@@ -491,12 +493,12 @@ program
|
|
|
491
493
|
type: 'list',
|
|
492
494
|
name: 'deploymentTarget',
|
|
493
495
|
message: 'Select Deployment Provider:',
|
|
494
|
-
choices: ['Vercel', '
|
|
496
|
+
choices: ['Vercel', 'None'],
|
|
495
497
|
default: 'None'
|
|
496
498
|
}
|
|
497
499
|
]);
|
|
498
500
|
// 4. Install Dependencies & Build (Required for Deployment)
|
|
499
|
-
if (deploymentTarget !== 'None') {
|
|
501
|
+
if (deploymentTarget !== 'None' && config.language !== types_1.ProjectLanguage.HTMLCSS) {
|
|
500
502
|
console.log();
|
|
501
503
|
const iSpinner = (0, ora_1.default)('Installing dependencies...').start();
|
|
502
504
|
try {
|
|
@@ -596,132 +598,6 @@ program
|
|
|
596
598
|
}
|
|
597
599
|
}
|
|
598
600
|
}
|
|
599
|
-
else if (deploymentTarget === 'Netlify') {
|
|
600
|
-
const netlifyManager = new NetlifyManager_1.NetlifyManager();
|
|
601
|
-
let netlifyInstalled = await netlifyManager.checkNetlifyInstalled();
|
|
602
|
-
if (!netlifyInstalled) {
|
|
603
|
-
console.log();
|
|
604
|
-
const { installNetlify } = await inquirer_1.default.prompt([
|
|
605
|
-
{
|
|
606
|
-
type: 'confirm',
|
|
607
|
-
name: 'installNetlify',
|
|
608
|
-
message: 'Netlify CLI not found. Install it globally?',
|
|
609
|
-
default: false
|
|
610
|
-
}
|
|
611
|
-
]);
|
|
612
|
-
if (installNetlify) {
|
|
613
|
-
const nSpinner = (0, ora_1.default)('Installing Netlify CLI...').start();
|
|
614
|
-
const installed = await netlifyManager.installGlobal();
|
|
615
|
-
if (installed) {
|
|
616
|
-
nSpinner.succeed(chalk_1.default.green('Netlify CLI installed!'));
|
|
617
|
-
netlifyInstalled = true;
|
|
618
|
-
}
|
|
619
|
-
else {
|
|
620
|
-
nSpinner.fail(chalk_1.default.red('Failed to install Netlify CLI.'));
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
if (netlifyInstalled) {
|
|
625
|
-
// Check Auth
|
|
626
|
-
const nSpinner = (0, ora_1.default)('Checking Netlify authentication...').start();
|
|
627
|
-
const loggedIn = await netlifyManager.checkAuth();
|
|
628
|
-
if (!loggedIn) {
|
|
629
|
-
nSpinner.stop();
|
|
630
|
-
console.log(chalk_1.default.yellow('\n Authentication required to deploy.'));
|
|
631
|
-
const { doLogin } = await inquirer_1.default.prompt([
|
|
632
|
-
{
|
|
633
|
-
type: 'confirm',
|
|
634
|
-
name: 'doLogin',
|
|
635
|
-
message: 'Log in to Netlify now?',
|
|
636
|
-
default: true
|
|
637
|
-
}
|
|
638
|
-
]);
|
|
639
|
-
if (doLogin) {
|
|
640
|
-
console.log(chalk_1.default.gray(' Opening Netlify login...'));
|
|
641
|
-
const loginSuccess = await netlifyManager.login();
|
|
642
|
-
if (!loginSuccess) {
|
|
643
|
-
console.log(chalk_1.default.red('\n Login failed. Skipping deployment.'));
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
|
-
console.log(chalk_1.default.green('\n Successfully logged in!'));
|
|
647
|
-
}
|
|
648
|
-
else {
|
|
649
|
-
console.log(chalk_1.default.yellow(' Skipping deployment (not logged in).'));
|
|
650
|
-
return;
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
else {
|
|
654
|
-
nSpinner.succeed(chalk_1.default.green('Authenticated with Netlify'));
|
|
655
|
-
}
|
|
656
|
-
const dSpinner = (0, ora_1.default)('Deploying to Netlify...').start();
|
|
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
|
-
}
|
|
712
|
-
const success = await netlifyManager.deploy(config.path);
|
|
713
|
-
if (success) {
|
|
714
|
-
console.log();
|
|
715
|
-
console.log(chalk_1.default.green(' ✔ Deployed to Netlify!'));
|
|
716
|
-
console.log(chalk_1.default.gray(' Run ') + chalk_1.default.cyan('netlify open') + chalk_1.default.gray(' to view your site.'));
|
|
717
|
-
}
|
|
718
|
-
else {
|
|
719
|
-
console.log();
|
|
720
|
-
console.log(chalk_1.default.red(' ✖ Deployment failed'));
|
|
721
|
-
console.log(chalk_1.default.gray(' Try running ') + chalk_1.default.cyan('netlify login') + chalk_1.default.gray(' and ') + chalk_1.default.cyan('netlify deploy') + chalk_1.default.gray(' manually.'));
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
601
|
if (!answers.createRemote || !githubToken && authMethod !== 'GitHub CLI') {
|
|
726
602
|
console.log(chalk_1.default.bold.yellow(' To push to GitHub later:'));
|
|
727
603
|
console.log(chalk_1.default.gray(' - Create a repository on GitHub'));
|
|
@@ -65,29 +65,30 @@ 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.
|
|
70
68
|
return new Promise((resolve) => {
|
|
71
69
|
const child = (0, child_process_1.spawn)('netlify', ['sites:create', '--name', name], {
|
|
72
70
|
cwd,
|
|
73
|
-
stdio: ['inherit', 'pipe', 'pipe']
|
|
71
|
+
stdio: ['inherit', 'pipe', 'pipe']
|
|
74
72
|
});
|
|
75
73
|
let output = '';
|
|
76
74
|
child.stdout?.on('data', (data) => {
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
const str = data.toString();
|
|
76
|
+
output += str;
|
|
77
|
+
process.stdout.write(data);
|
|
79
78
|
});
|
|
80
79
|
child.stderr?.on('data', (data) => {
|
|
81
80
|
output += data.toString();
|
|
82
|
-
process.stderr.write(data);
|
|
81
|
+
process.stderr.write(data);
|
|
83
82
|
});
|
|
84
83
|
child.on('close', (code) => {
|
|
85
84
|
if (code === 0) {
|
|
86
|
-
|
|
85
|
+
// Extract Site ID/Project ID
|
|
86
|
+
// Format: "Site ID: 022..." or "Project ID: 022..."
|
|
87
|
+
const idMatch = output.match(/(?:Site|Project)\s+ID:\s+([a-zA-Z0-9-]+)/i);
|
|
88
|
+
const siteId = idMatch ? idMatch[1] : undefined;
|
|
89
|
+
resolve({ success: true, siteId });
|
|
87
90
|
}
|
|
88
91
|
else {
|
|
89
|
-
// Check for common "name taken" indicators in Netlify CLI output
|
|
90
|
-
// "Already exists" or "name is already taken"
|
|
91
92
|
if (output.toLowerCase().includes('already exists') || output.toLowerCase().includes('taken')) {
|
|
92
93
|
resolve({ success: false, reason: 'taken' });
|
|
93
94
|
}
|
|
@@ -103,10 +104,13 @@ class NetlifyManager {
|
|
|
103
104
|
return { success: false, reason: 'error' };
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
|
-
async deploy(cwd) {
|
|
107
|
+
async deploy(cwd, siteId) {
|
|
107
108
|
return new Promise((resolve) => {
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
const args = ['deploy', '--prod'];
|
|
110
|
+
if (siteId) {
|
|
111
|
+
args.push('--site', siteId);
|
|
112
|
+
}
|
|
113
|
+
const child = (0, child_process_1.spawn)('netlify', args, {
|
|
110
114
|
cwd,
|
|
111
115
|
stdio: 'inherit'
|
|
112
116
|
});
|
|
@@ -66,6 +66,9 @@ class TemplateManager {
|
|
|
66
66
|
case types_1.ProjectLanguage.React:
|
|
67
67
|
await this.createReactStructure(config);
|
|
68
68
|
break;
|
|
69
|
+
case types_1.ProjectLanguage.HTMLCSS:
|
|
70
|
+
await this.createHTMLCSSStructure(config);
|
|
71
|
+
break;
|
|
69
72
|
}
|
|
70
73
|
}
|
|
71
74
|
createTypeSpecificStructure(config) {
|
|
@@ -327,6 +330,59 @@ export default defineConfig({
|
|
|
327
330
|
`);
|
|
328
331
|
this.createNetlifyConfig(config);
|
|
329
332
|
}
|
|
333
|
+
async createHTMLCSSStructure(config) {
|
|
334
|
+
fs.writeFileSync(path.join(config.path, 'index.html'), `
|
|
335
|
+
<!DOCTYPE html>
|
|
336
|
+
<html lang="en">
|
|
337
|
+
<head>
|
|
338
|
+
<meta charset="UTF-8">
|
|
339
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
340
|
+
<title>${config.name}</title>
|
|
341
|
+
<link rel="stylesheet" href="style.css">
|
|
342
|
+
</head>
|
|
343
|
+
<body>
|
|
344
|
+
<div class="container">
|
|
345
|
+
<h1>Welcome to ${config.name}</h1>
|
|
346
|
+
<p>This is a simple HTML/CSS project created by ZeroStart CLI!</p>
|
|
347
|
+
</div>
|
|
348
|
+
<script src="script.js"></script>
|
|
349
|
+
</body>
|
|
350
|
+
</html>
|
|
351
|
+
`);
|
|
352
|
+
fs.writeFileSync(path.join(config.path, 'style.css'), `
|
|
353
|
+
body {
|
|
354
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
355
|
+
background-color: #f4f4f9;
|
|
356
|
+
color: #333;
|
|
357
|
+
display: flex;
|
|
358
|
+
justify-content: center;
|
|
359
|
+
align-items: center;
|
|
360
|
+
height: 100vh;
|
|
361
|
+
margin: 0;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.container {
|
|
365
|
+
text-align: center;
|
|
366
|
+
background: white;
|
|
367
|
+
padding: 2rem;
|
|
368
|
+
border-radius: 8px;
|
|
369
|
+
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
h1 {
|
|
373
|
+
color: #007bff;
|
|
374
|
+
}
|
|
375
|
+
`);
|
|
376
|
+
fs.writeFileSync(path.join(config.path, 'script.js'), `
|
|
377
|
+
console.log("${config.name} initialized!");
|
|
378
|
+
`);
|
|
379
|
+
// No netlify.toml needed for static sites if direct deploy path is known,
|
|
380
|
+
// but we'll create a simple one for consistency.
|
|
381
|
+
const netlifyContent = `[build]
|
|
382
|
+
publish = "."
|
|
383
|
+
`;
|
|
384
|
+
fs.writeFileSync(path.join(config.path, 'netlify.toml'), netlifyContent);
|
|
385
|
+
}
|
|
330
386
|
createNetlifyConfig(config) {
|
|
331
387
|
// Only relevant for web apps (React, etc)
|
|
332
388
|
// For now, we assume Vite + React
|
package/out/types.js
CHANGED
|
@@ -5,6 +5,7 @@ var ProjectLanguage;
|
|
|
5
5
|
(function (ProjectLanguage) {
|
|
6
6
|
ProjectLanguage["NodeJS"] = "Node.js";
|
|
7
7
|
ProjectLanguage["React"] = "React";
|
|
8
|
+
ProjectLanguage["HTMLCSS"] = "HTML/CSS";
|
|
8
9
|
ProjectLanguage["Python"] = "Python";
|
|
9
10
|
ProjectLanguage["Java"] = "Java";
|
|
10
11
|
ProjectLanguage["CPP"] = "C++";
|