blok0 0.1.1 → 0.1.3
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/dist/handlers/add-block.js +29 -36
- package/dist/handlers/generate.js +18 -35
- package/dist/handlers/login.js +20 -29
- package/dist/index.js +25 -18
- package/dist/ui.d.ts +77 -0
- package/dist/ui.js +222 -0
- package/package.json +37 -33
- package/src/detectors.ts +22 -22
- package/src/handlers/add-block.ts +53 -40
- package/src/handlers/generate.ts +59 -62
- package/src/handlers/login.ts +40 -29
- package/src/index.ts +34 -18
- package/src/ui.ts +234 -0
- package/tsconfig.json +16 -16
package/dist/ui.js
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.log = exports.ProgressBar = exports.Spinner = exports.ciMode = exports.noEmoji = exports.noAnimation = exports.isTTY = exports.colors = exports.EMOJIS = void 0;
|
|
7
|
+
exports.setUIFlags = setUIFlags;
|
|
8
|
+
exports.withSpinner = withSpinner;
|
|
9
|
+
exports.showSection = showSection;
|
|
10
|
+
exports.showNextSteps = showNextSteps;
|
|
11
|
+
const ora_1 = __importDefault(require("ora"));
|
|
12
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
13
|
+
const cli_progress_1 = require("cli-progress");
|
|
14
|
+
// Emoji constants for consistent usage
|
|
15
|
+
exports.EMOJIS = {
|
|
16
|
+
SUCCESS: '✅',
|
|
17
|
+
ERROR: '❌',
|
|
18
|
+
WARNING: '⚠️',
|
|
19
|
+
INFO: 'ℹ️',
|
|
20
|
+
LOCK: '🔐',
|
|
21
|
+
PACKAGE: '📦',
|
|
22
|
+
FOLDER: '📁',
|
|
23
|
+
GEAR: '🔧',
|
|
24
|
+
SEARCH: '🔍',
|
|
25
|
+
ROCKET: '🚀',
|
|
26
|
+
DOWNLOAD: '📥',
|
|
27
|
+
PARTY: '🎉',
|
|
28
|
+
WRENCH: '🔧',
|
|
29
|
+
CHECK: '✅',
|
|
30
|
+
CROSS: '❌',
|
|
31
|
+
ARROW: '→',
|
|
32
|
+
};
|
|
33
|
+
// Color utilities
|
|
34
|
+
exports.colors = {
|
|
35
|
+
success: chalk_1.default.green,
|
|
36
|
+
error: chalk_1.default.red,
|
|
37
|
+
warning: chalk_1.default.yellow,
|
|
38
|
+
info: chalk_1.default.blue,
|
|
39
|
+
accent: chalk_1.default.cyan,
|
|
40
|
+
muted: chalk_1.default.gray,
|
|
41
|
+
};
|
|
42
|
+
// Check if we're in a TTY environment
|
|
43
|
+
exports.isTTY = process.stdout.isTTY;
|
|
44
|
+
// Global flags for disabling features
|
|
45
|
+
exports.noAnimation = false;
|
|
46
|
+
exports.noEmoji = false;
|
|
47
|
+
exports.ciMode = false;
|
|
48
|
+
// Set global flags
|
|
49
|
+
function setUIFlags(flags) {
|
|
50
|
+
exports.noAnimation = flags.noAnimation || exports.ciMode;
|
|
51
|
+
exports.noEmoji = flags.noEmoji || exports.ciMode;
|
|
52
|
+
exports.ciMode = flags.ci || false;
|
|
53
|
+
}
|
|
54
|
+
// Apply emoji settings to text
|
|
55
|
+
function applyEmoji(text, emoji) {
|
|
56
|
+
if (exports.noEmoji || !emoji)
|
|
57
|
+
return text;
|
|
58
|
+
return `${emoji} ${text}`;
|
|
59
|
+
}
|
|
60
|
+
// Spinner wrapper with TTY detection
|
|
61
|
+
class Spinner {
|
|
62
|
+
text;
|
|
63
|
+
emoji;
|
|
64
|
+
spinner = null;
|
|
65
|
+
startTime = 0;
|
|
66
|
+
constructor(text, emoji) {
|
|
67
|
+
this.text = text;
|
|
68
|
+
this.emoji = emoji;
|
|
69
|
+
}
|
|
70
|
+
start() {
|
|
71
|
+
if (!exports.isTTY || exports.noAnimation) {
|
|
72
|
+
console.log(applyEmoji(this.text, this.emoji));
|
|
73
|
+
this.startTime = Date.now();
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
this.spinner = (0, ora_1.default)({
|
|
77
|
+
text: applyEmoji(this.text, this.emoji),
|
|
78
|
+
spinner: 'dots',
|
|
79
|
+
}).start();
|
|
80
|
+
this.startTime = Date.now();
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
update(text, emoji) {
|
|
84
|
+
if (!this.spinner) {
|
|
85
|
+
if (exports.isTTY && !exports.noAnimation) {
|
|
86
|
+
this.spinner = (0, ora_1.default)(applyEmoji(text, emoji)).start();
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
console.log(applyEmoji(text, emoji));
|
|
90
|
+
}
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
this.spinner.text = applyEmoji(text, emoji || this.emoji);
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
succeed(text) {
|
|
97
|
+
const duration = this.getDuration();
|
|
98
|
+
const successText = text || this.text;
|
|
99
|
+
const durationInfo = duration > 1000 ? ` (${duration}ms)` : '';
|
|
100
|
+
if (this.spinner) {
|
|
101
|
+
this.spinner.succeed(applyEmoji(successText, exports.EMOJIS.SUCCESS) + durationInfo);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
console.log(applyEmoji(successText + durationInfo, exports.EMOJIS.SUCCESS));
|
|
105
|
+
}
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
fail(text) {
|
|
109
|
+
const failText = text || this.text;
|
|
110
|
+
if (this.spinner) {
|
|
111
|
+
this.spinner.fail(applyEmoji(failText, exports.EMOJIS.ERROR));
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
console.error(applyEmoji(failText, exports.EMOJIS.ERROR));
|
|
115
|
+
}
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
stop() {
|
|
119
|
+
if (this.spinner) {
|
|
120
|
+
this.spinner.stop();
|
|
121
|
+
}
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
124
|
+
getDuration() {
|
|
125
|
+
return Date.now() - this.startTime;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
exports.Spinner = Spinner;
|
|
129
|
+
// Utility function to wrap async operations with spinner
|
|
130
|
+
async function withSpinner(text, operation, options = {}) {
|
|
131
|
+
const spinner = new Spinner(text, options.emoji);
|
|
132
|
+
spinner.start();
|
|
133
|
+
try {
|
|
134
|
+
const result = await operation();
|
|
135
|
+
spinner.succeed(options.successText);
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
spinner.fail(options.failText);
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Progress bar utilities
|
|
144
|
+
class ProgressBar {
|
|
145
|
+
options;
|
|
146
|
+
bar = null;
|
|
147
|
+
constructor(options) {
|
|
148
|
+
this.options = options;
|
|
149
|
+
}
|
|
150
|
+
start() {
|
|
151
|
+
if (!exports.isTTY || exports.noAnimation) {
|
|
152
|
+
if (this.options.title) {
|
|
153
|
+
console.log(this.options.title);
|
|
154
|
+
}
|
|
155
|
+
return this;
|
|
156
|
+
}
|
|
157
|
+
this.bar = new cli_progress_1.SingleBar({
|
|
158
|
+
format: this.options.format || '{bar} {percentage}% | {value}/{total} | {eta}s',
|
|
159
|
+
barCompleteChar: '\u2588',
|
|
160
|
+
barIncompleteChar: '\u2591',
|
|
161
|
+
hideCursor: true,
|
|
162
|
+
});
|
|
163
|
+
if (this.options.title) {
|
|
164
|
+
console.log(this.options.title);
|
|
165
|
+
}
|
|
166
|
+
this.bar.start(this.options.total, 0);
|
|
167
|
+
return this;
|
|
168
|
+
}
|
|
169
|
+
update(current) {
|
|
170
|
+
if (this.bar) {
|
|
171
|
+
this.bar.update(current);
|
|
172
|
+
}
|
|
173
|
+
else if (exports.isTTY && !exports.noAnimation) {
|
|
174
|
+
// Fallback to simple progress if bar not initialized
|
|
175
|
+
const percent = Math.round((current / this.options.total) * 100);
|
|
176
|
+
process.stdout.write(`\r${this.options.title || 'Progress'}: ${percent}% (${current}/${this.options.total})`);
|
|
177
|
+
}
|
|
178
|
+
return this;
|
|
179
|
+
}
|
|
180
|
+
increment(amount = 1) {
|
|
181
|
+
if (this.bar) {
|
|
182
|
+
this.bar.increment(amount);
|
|
183
|
+
}
|
|
184
|
+
return this;
|
|
185
|
+
}
|
|
186
|
+
stop() {
|
|
187
|
+
if (this.bar) {
|
|
188
|
+
this.bar.stop();
|
|
189
|
+
}
|
|
190
|
+
else if (exports.isTTY && !exports.noAnimation) {
|
|
191
|
+
process.stdout.write('\n');
|
|
192
|
+
}
|
|
193
|
+
return this;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
exports.ProgressBar = ProgressBar;
|
|
197
|
+
// Enhanced console methods
|
|
198
|
+
exports.log = {
|
|
199
|
+
success: (text) => console.log(exports.colors.success(applyEmoji(text, exports.EMOJIS.SUCCESS))),
|
|
200
|
+
error: (text) => console.error(exports.colors.error(applyEmoji(text, exports.EMOJIS.ERROR))),
|
|
201
|
+
warning: (text) => console.warn(exports.colors.warning(applyEmoji(text, exports.EMOJIS.WARNING))),
|
|
202
|
+
info: (text) => console.log(exports.colors.info(applyEmoji(text, exports.EMOJIS.INFO))),
|
|
203
|
+
plain: (text) => console.log(text),
|
|
204
|
+
header: (text) => console.log(exports.colors.accent(`\n${text}\n`)),
|
|
205
|
+
step: (step, total, text) => {
|
|
206
|
+
const stepText = `${step}/${total}`;
|
|
207
|
+
console.log(exports.colors.muted(`[${stepText}]`) + ' ' + text);
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
// Utility to show a section header
|
|
211
|
+
function showSection(title, emoji) {
|
|
212
|
+
console.log('\n' + exports.colors.accent('='.repeat(50)));
|
|
213
|
+
console.log(applyEmoji(title, emoji));
|
|
214
|
+
console.log(exports.colors.accent('='.repeat(50)) + '\n');
|
|
215
|
+
}
|
|
216
|
+
// Utility to show next steps
|
|
217
|
+
function showNextSteps(steps) {
|
|
218
|
+
console.log(exports.colors.accent('\nNext steps:'));
|
|
219
|
+
steps.forEach((step, index) => {
|
|
220
|
+
console.log(` ${index + 1}. ${step}`);
|
|
221
|
+
});
|
|
222
|
+
}
|
package/package.json
CHANGED
|
@@ -1,33 +1,37 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "blok0",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "CLI tool for Payload CMS scaffolding",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"blok0": "./dist/index.js"
|
|
8
|
-
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"build": "bun run tsc",
|
|
11
|
-
"dev": "bun run tsx src/index.ts"
|
|
12
|
-
},
|
|
13
|
-
"keywords": [
|
|
14
|
-
"cli",
|
|
15
|
-
"payload",
|
|
16
|
-
"cms"
|
|
17
|
-
],
|
|
18
|
-
"author": "Your Name",
|
|
19
|
-
"license": "MIT",
|
|
20
|
-
"dependencies": {
|
|
21
|
-
"axios": "^1.13.2",
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "blok0",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "CLI tool for Payload CMS scaffolding",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"blok0": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "bun run tsc",
|
|
11
|
+
"dev": "bun run tsx src/index.ts"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"cli",
|
|
15
|
+
"payload",
|
|
16
|
+
"cms"
|
|
17
|
+
],
|
|
18
|
+
"author": "Your Name",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"axios": "^1.13.2",
|
|
22
|
+
"chalk": "^5.6.2",
|
|
23
|
+
"cli-progress": "^3.12.0",
|
|
24
|
+
"keytar": "^7.9.0",
|
|
25
|
+
"open": "^11.0.0",
|
|
26
|
+
"ora": "^9.0.0",
|
|
27
|
+
"payload": "^2.0.0",
|
|
28
|
+
"ts-morph": "^27.0.2"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/cli-progress": "^3.11.6",
|
|
32
|
+
"@types/node": "^20.19.27",
|
|
33
|
+
"bun-types": "^1.0.0",
|
|
34
|
+
"tsx": "^4.0.0",
|
|
35
|
+
"typescript": "^5.0.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
package/src/detectors.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { existsSync } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
|
|
4
|
-
export function checkEmptyDirectory(): boolean {
|
|
5
|
-
const cwd = process.cwd();
|
|
6
|
-
|
|
7
|
-
const pkgPath = join(cwd, 'package.json');
|
|
8
|
-
const configJs = join(cwd, 'payload.config.js');
|
|
9
|
-
const configTs = join(cwd, 'payload.config.ts');
|
|
10
|
-
|
|
11
|
-
if (existsSync(pkgPath)) {
|
|
12
|
-
console.error('Error: package.json already exists. Please run in an empty directory.');
|
|
13
|
-
return false;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (existsSync(configJs) || existsSync(configTs)) {
|
|
17
|
-
console.error('Error: Payload config file already exists. Please run in an empty directory.');
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
|
|
4
|
+
export function checkEmptyDirectory(): boolean {
|
|
5
|
+
const cwd = process.cwd();
|
|
6
|
+
|
|
7
|
+
const pkgPath = join(cwd, 'package.json');
|
|
8
|
+
const configJs = join(cwd, 'payload.config.js');
|
|
9
|
+
const configTs = join(cwd, 'payload.config.ts');
|
|
10
|
+
|
|
11
|
+
if (existsSync(pkgPath)) {
|
|
12
|
+
console.error('Error: package.json already exists. Please run in an empty directory.');
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (existsSync(configJs) || existsSync(configTs)) {
|
|
17
|
+
console.error('Error: Payload config file already exists. Please run in an empty directory.');
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
@@ -10,40 +10,47 @@ import {
|
|
|
10
10
|
validateBlockDirectory
|
|
11
11
|
} from '../blocks';
|
|
12
12
|
import { updatePageCollectionConfig, updateRenderBlocksComponent, findPagesCollection, findRenderBlocksComponent } from '../ast';
|
|
13
|
+
import { withSpinner, log, showSection, showNextSteps, EMOJIS, ProgressBar } from '../ui';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Handle add block command
|
|
16
17
|
*/
|
|
17
18
|
export async function handleAddBlock(blockUrl: string, options: { force?: boolean; dryRun?: boolean } = {}): Promise<void> {
|
|
18
|
-
|
|
19
|
-
console.log('====================');
|
|
20
|
-
console.log('');
|
|
19
|
+
showSection('📦 Adding Blok0 Block', EMOJIS.PACKAGE);
|
|
21
20
|
|
|
22
21
|
try {
|
|
23
22
|
// Step 1: Authentication check
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const authenticated = await withSpinner(
|
|
24
|
+
'Checking authentication',
|
|
25
|
+
() => isAuthenticated(),
|
|
26
|
+
{ emoji: EMOJIS.LOCK }
|
|
27
|
+
);
|
|
28
|
+
|
|
26
29
|
if (!authenticated) {
|
|
27
|
-
|
|
30
|
+
log.error('You are not logged in. Please run `blok0 login` first.');
|
|
28
31
|
process.exit(1);
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
// Step 2: Fetch block data from API
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
const { metadata, files } = await withSpinner(
|
|
36
|
+
`Fetching block from ${blockUrl}`,
|
|
37
|
+
() => apiClient.fetchBlockData(blockUrl),
|
|
38
|
+
{ emoji: EMOJIS.SEARCH }
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
log.success(`Found block: "${metadata.name}" (${metadata.slug})`);
|
|
35
42
|
|
|
36
43
|
// Step 3: Check if block is already registered
|
|
37
44
|
if (isBlockRegistered(metadata.slug)) {
|
|
38
45
|
if (!options.force) {
|
|
39
|
-
|
|
46
|
+
log.error(`Block "${metadata.slug}" is already installed. Use --force to reinstall.`);
|
|
40
47
|
process.exit(1);
|
|
41
48
|
}
|
|
42
|
-
|
|
49
|
+
log.warning('Block already exists, reinstalling...');
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
if (options.dryRun) {
|
|
46
|
-
|
|
53
|
+
log.info('Dry run mode - would perform the following actions:');
|
|
47
54
|
console.log(` - Create directory: src/blocks/${metadata.slug}`);
|
|
48
55
|
console.log(` - Download ${files.length} files`);
|
|
49
56
|
console.log(' - Update Payload config');
|
|
@@ -56,14 +63,13 @@ export async function handleAddBlock(blockUrl: string, options: { force?: boolea
|
|
|
56
63
|
const blocksDir = ensureBlocksDirectory();
|
|
57
64
|
|
|
58
65
|
// Step 5: Create block directory and files
|
|
59
|
-
console.log('📁 Creating block directory and files...');
|
|
60
66
|
const { dir, configPath, componentPath } = createBlockDirectory(blocksDir, metadata.slug, files);
|
|
61
|
-
|
|
67
|
+
log.success(`Created block directory: ${path.relative(process.cwd(), dir)}`);
|
|
62
68
|
|
|
63
69
|
// Step 6: Validate created block
|
|
64
70
|
const validation = validateBlockDirectory(dir);
|
|
65
71
|
if (!validation.valid) {
|
|
66
|
-
|
|
72
|
+
log.error('Block validation failed:');
|
|
67
73
|
validation.errors.forEach(error => console.error(` - ${error}`));
|
|
68
74
|
// Cleanup on failure
|
|
69
75
|
require('fs').rmSync(dir, { recursive: true, force: true });
|
|
@@ -90,43 +96,50 @@ export async function handleAddBlock(blockUrl: string, options: { force?: boolea
|
|
|
90
96
|
// Step 9: Update Pages collection (AST manipulation)
|
|
91
97
|
const pagesCollectionPath = findPagesCollection();
|
|
92
98
|
if (pagesCollectionPath) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
await withSpinner(
|
|
100
|
+
'Updating Pages collection',
|
|
101
|
+
async () => {
|
|
102
|
+
const blockIdentifier = slugToIdentifier(metadata.slug);
|
|
103
|
+
const relativeConfigPath = `@/blocks/${metadata.slug}/config`;
|
|
104
|
+
updatePageCollectionConfig(pagesCollectionPath, relativeConfigPath, blockIdentifier);
|
|
105
|
+
},
|
|
106
|
+
{ emoji: EMOJIS.GEAR, successText: `Added ${slugToIdentifier(metadata.slug)} to Pages collection` }
|
|
107
|
+
);
|
|
99
108
|
} else {
|
|
100
|
-
|
|
109
|
+
log.warning('Could not find Pages collection file. You may need to manually add the block to your collections.');
|
|
101
110
|
}
|
|
102
111
|
|
|
103
112
|
// Step 10: Update RenderBlocks component (AST manipulation)
|
|
104
113
|
const renderBlocksPath = findRenderBlocksComponent();
|
|
105
114
|
if (renderBlocksPath) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
115
|
+
await withSpinner(
|
|
116
|
+
'Updating RenderBlocks component',
|
|
117
|
+
async () => {
|
|
118
|
+
const relativeComponentPath = `./${metadata.slug}/Component`;
|
|
119
|
+
updateRenderBlocksComponent(renderBlocksPath, metadata.slug, relativeComponentPath);
|
|
120
|
+
},
|
|
121
|
+
{ emoji: EMOJIS.GEAR, successText: `Added ${metadata.slug} component to RenderBlocks` }
|
|
122
|
+
);
|
|
111
123
|
} else {
|
|
112
|
-
|
|
124
|
+
log.warning('Could not find RenderBlocks component. You may need to manually add the block component.');
|
|
113
125
|
}
|
|
114
126
|
|
|
115
127
|
// Step 11: Register block in registry
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
128
|
+
await withSpinner(
|
|
129
|
+
'Registering block',
|
|
130
|
+
async () => addBlockToRegistry(blockEntry),
|
|
131
|
+
{ emoji: EMOJIS.CHECK, successText: 'Block registered successfully' }
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
log.success('Block installation complete!');
|
|
135
|
+
showNextSteps([
|
|
136
|
+
`Review the installed files in src/blocks/${metadata.slug}`,
|
|
137
|
+
'Test your application to ensure the block works correctly',
|
|
138
|
+
'Commit the changes to your repository'
|
|
139
|
+
]);
|
|
127
140
|
|
|
128
141
|
} catch (error) {
|
|
129
142
|
console.error('❌ Failed to add block:', (error as Error).message);
|
|
130
143
|
process.exit(1);
|
|
131
144
|
}
|
|
132
|
-
}
|
|
145
|
+
}
|
package/src/handlers/generate.ts
CHANGED
|
@@ -1,62 +1,59 @@
|
|
|
1
|
-
import { createInterface } from 'readline';
|
|
2
|
-
import { exec, spawn } from 'child_process';
|
|
3
|
-
import { promisify } from 'util';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
console.log('Blok0 starter project created successfully!');
|
|
62
|
-
}
|
|
1
|
+
import { createInterface } from 'readline';
|
|
2
|
+
import { exec, spawn } from 'child_process';
|
|
3
|
+
import { promisify } from 'util';
|
|
4
|
+
import { withSpinner, log, showNextSteps, EMOJIS } from '../ui';
|
|
5
|
+
|
|
6
|
+
const execAsync = promisify(exec);
|
|
7
|
+
|
|
8
|
+
const repoUrl = 'https://github.com/blok0-payload/starter.git';
|
|
9
|
+
|
|
10
|
+
function prompt(question: string): Promise<boolean> {
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
const rl = createInterface({
|
|
13
|
+
input: process.stdin,
|
|
14
|
+
output: process.stdout,
|
|
15
|
+
});
|
|
16
|
+
rl.question(question, (answer) => {
|
|
17
|
+
rl.close();
|
|
18
|
+
resolve(answer.toLowerCase().startsWith('y'));
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function generateStarter(): Promise<void> {
|
|
24
|
+
log.header('🚀 Setting up Blok0 starter project...');
|
|
25
|
+
|
|
26
|
+
// Clone repository with spinner
|
|
27
|
+
await withSpinner(
|
|
28
|
+
'Cloning starter repository',
|
|
29
|
+
async () => {
|
|
30
|
+
await execAsync(`git clone --depth 1 ${repoUrl} .`);
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
emoji: EMOJIS.DOWNLOAD,
|
|
34
|
+
successText: 'Repository cloned successfully'
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Prompt for git init
|
|
39
|
+
const initGit = await prompt('Initialize git repository? (y/n): ');
|
|
40
|
+
if (initGit) {
|
|
41
|
+
await withSpinner(
|
|
42
|
+
'Initializing git repository',
|
|
43
|
+
async () => {
|
|
44
|
+
await execAsync('git init');
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
emoji: EMOJIS.GEAR,
|
|
48
|
+
successText: 'Git repository initialized'
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
log.success('Starter project ready!');
|
|
54
|
+
showNextSteps([
|
|
55
|
+
'Run \'npm install\' or \'bun install\' to install dependencies',
|
|
56
|
+
'Start developing your Blok0 x PayloadCMS project'
|
|
57
|
+
]);
|
|
58
|
+
}
|
|
59
|
+
|