create-letswhim 2.0.3 → 2.0.5

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.
Files changed (2) hide show
  1. package/index.js +327 -71
  2. package/package.json +19 -10
package/index.js CHANGED
@@ -2,103 +2,359 @@
2
2
  import { execSync } from 'child_process';
3
3
  import path from 'path';
4
4
  import fs from 'fs';
5
- import pc from 'picocolors'; // Menggunakan picocolors
6
- import prompts from 'prompts'; // Menggunakan prompts
7
-
8
- // --- FRAME 1: Orang Jalan di Tempat (NASA-Blue/Cyan) ---
9
- const walkingFrames = [
10
- ` ${pc.cyan(' ( ) ')}
11
- ${pc.cyan(' /| |\\ ')}
12
- ${pc.cyan(' / \\ ')}
13
- ${pc.blue('~~~~~~~~~~')}`,
14
- ` ${pc.cyan(' ( ) ')}
15
- ${pc.cyan(' | |/ ')}
16
- ${pc.cyan(' | | ')}
17
- ${pc.blue('~~~~~~~~~~')}`
18
- ];
19
-
20
- // --- FRAME 2: Orang Menerjang Ombak (Success) ---
21
- const surfingArt = `
22
- ${pc.cyan(' ( ) /')}
23
- ${pc.cyan(' /| |/')}
24
- ${pc.cyan(' /_|_|/')} ${pc.white("let's just start")}
25
- ${pc.blue('~')} ${pc.cyan(' / \\')}
26
- ${pc.blue('~~~')} ${pc.cyan(' / \\')}
27
- ${pc.blue('~~~~~')} ${pc.cyan(' / \\')}
28
- ${pc.blue('~~~~~~~~~~~~~~~~~~~~~~~~~~~~')}
29
- `;
30
-
31
- async function startLoading(message) {
32
- const total = 40;
33
- let frameIndex = 0;
34
- console.log('\n\n\n\n');
35
-
36
- for (let i = 0; i <= total; i++) {
37
- const percent = Math.round((i / total) * 100);
38
- // Ganti pc.blue ke pc.cyan biar gak ungu!
39
- const bar = pc.cyan('█').repeat(i) + pc.gray('░').repeat(total - i);
5
+ import pc from 'picocolors';
6
+ import prompts from 'prompts';
7
+
8
+ // ==================== CONFIGURATION ====================
9
+ const CONFIG = {
10
+ repoUrl: "https://github.com/ripkiiii/letswhim-starter.git",
11
+ version: "2.0.5",
12
+ colors: {
13
+ primary: pc.cyan,
14
+ secondary: pc.blue,
15
+ success: pc.green,
16
+ warning: pc.yellow,
17
+ error: pc.red,
18
+ muted: pc.gray,
19
+ highlight: pc.white
20
+ }
21
+ };
22
+
23
+ // ==================== ASCII ART LIBRARY ====================
24
+ const ASCII = {
25
+ // Loading frames dengan lebih banyak variasi
26
+ walkingFrames: [
27
+ // Frame 1 - Normal walk
28
+ [
29
+ ` ${pc.cyan(' ╭┴┴╮ ')}`,
30
+ ` ${pc.cyan(' │• •│ ')}`,
31
+ ` ${pc.cyan(' │ │ ')}`,
32
+ ` ${pc.cyan(' ╰┬┬┬╯ ')}`,
33
+ ` ${pc.blue('░░░░░░░░░░')}`
34
+ ].join('\n'),
35
+
36
+ // Frame 2 - Right foot forward
37
+ [
38
+ ` ${pc.cyan(' ╭┴┴╮ ')}`,
39
+ ` ${pc.cyan(' │• •│ ')}`,
40
+ ` ${pc.cyan(' │┌─┘ ')}`,
41
+ ` ${pc.cyan(' ╰┬┴┐ ')}`,
42
+ ` ${pc.blue('░░░░░░░░░░')}`
43
+ ].join('\n'),
44
+
45
+ // Frame 3 - Left foot forward
46
+ [
47
+ ` ${pc.cyan(' ╭┴┴╮ ')}`,
48
+ ` ${pc.cyan(' │• •│ ')}`,
49
+ ` ${pc.cyan(' │└─┐ ')}`,
50
+ ` ${pc.cyan(' ╰┴┬┘ ')}`,
51
+ ` ${pc.blue('░░░░░░░░░░')}`
52
+ ].join('\n'),
53
+
54
+ // Frame 4 - Carrying board
55
+ [
56
+ ` ${pc.cyan(' ╭┴┴╮ ')} ${pc.white('__')}`,
57
+ ` ${pc.cyan(' │• •│ ') + pc.white('_/ \\_')}`,
58
+ ` ${pc.cyan(' │ _ │') + pc.white('/ \\')}`,
59
+ ` ${pc.cyan(' ╰─┬─╯') + pc.white('\\______/')}`,
60
+ ` ${pc.blue('░░░░░░░░░░░░░░░')}`
61
+ ].join('\n')
62
+ ],
63
+
64
+ // Surfing animation frames
65
+ surfingFrames: [
66
+ // Frame 1 - Start surfing
67
+ [
68
+ ` ${pc.cyan(' ╭┴┴╮ ')}`,
69
+ ` ${pc.cyan(' │• •│ ')}`,
70
+ ` ${pc.cyan(' ╰─┬─╯ ')}`,
71
+ ` ${pc.cyan(' _/')} ${pc.white('|')} ${pc.cyan('\\_ ')}`,
72
+ ` ${pc.blue('~~')} ${pc.white('( )')} ${pc.white('Catching waves...')}`,
73
+ ` ${pc.blue('~~~~~')}${pc.white('/| |\\')}`,
74
+ `${pc.blue('~~~~~~~~~~~')}`,
75
+ `${pc.blue('~~~~~~~~~~~~~~~~~~~~~~')}`
76
+ ].join('\n'),
77
+
78
+ // Frame 2 - Riding wave
79
+ [
80
+ ` ${pc.cyan(' ╭┴┴╮ ')}`,
81
+ ` ${pc.cyan(' │• •│ 🏄')}`,
82
+ ` ${pc.cyan(' ╰─┬─╯ ')}`,
83
+ ` ${pc.cyan(' __/')}${pc.white('|')}${pc.cyan('\\__ ')}`,
84
+ ` ${pc.blue('~~~')} ${pc.white('( )')} ${pc.white('Riding the wave!')}`,
85
+ `${pc.blue('~~~~~~')}${pc.white('/| |\\')}`,
86
+ `${pc.blue('~~~~~~~~~~~~')}`,
87
+ `${pc.blue('~~~~~~~~~~~~~~~~~~~~~~~')}`
88
+ ].join('\n'),
89
+
90
+ // Frame 3 - Big wave
91
+ [
92
+ ` ${pc.cyan(' ╭┴┴╮ ')}`,
93
+ ` ${pc.cyan(' │• •│ 🌊')}`,
94
+ ` ${pc.cyan(' ╰─┬─╯ ')}`,
95
+ ` ${pc.cyan(' ___/')}${pc.white('|')}${pc.cyan('\\___')}`,
96
+ ` ${pc.blue('~~~~')}${pc.white('( )')} ${pc.white('Big one coming!')}`,
97
+ `${pc.blue('~~~~~~~')}${pc.white('/| |\\')}`,
98
+ `${pc.blue('~~~~~~~~~~~~~~')}`,
99
+ `${pc.blue('~~~~~~~~~~~~~~~~~~~~~~~~')}`
100
+ ].join('\n')
101
+ ],
102
+
103
+ // Success screen dengan efek 3D
104
+ successArt: [
105
+ `${pc.cyan('╔═══════════════════════════════════════╗')}`,
106
+ `${pc.cyan('║')} ${pc.green('✨ PROJECT READY TO SURF! ✨')} ${pc.cyan('║')}`,
107
+ `${pc.cyan('╠═══════════════════════════════════════╣')}`,
108
+ ` `,
109
+ ` ${pc.cyan('\\')} ${pc.cyan('/')} `,
110
+ ` ${pc.cyan('\\')} ${pc.white('O')} ${pc.cyan('/')} `,
111
+ ` ${pc.cyan('\\')} ${pc.white('K')} ${pc.cyan('/')} `,
112
+ ` ${pc.cyan('╲')}${pc.white('!')}${pc.cyan('╱')} `,
113
+ ` ${pc.cyan('●')} `,
114
+ ` ${pc.blue('/')} ${pc.white('|')} ${pc.blue('\\')} `,
115
+ ` ${pc.blue('/')} ${pc.white('|')} ${pc.blue('\\')} `,
116
+ ` ${pc.blue('/')} ${pc.white('|')} ${pc.blue('\\')} `,
117
+ `${pc.blue('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')}`,
118
+ ` `,
119
+ `${pc.cyan('╚═══════════════════════════════════════╝')}`
120
+ ].join('\n')
121
+ };
122
+
123
+ // ==================== ANIMATION ENGINE ====================
124
+ class AnimationEngine {
125
+ constructor() {
126
+ this.spinnerIndex = 0;
127
+ this.progress = 0;
128
+ this.isAnimating = false;
129
+ }
130
+
131
+ clearLines(count = 1) {
132
+ process.stdout.write('\u001b[1A\u001b[2K'.repeat(count));
133
+ }
134
+
135
+ createProgressBar(percent, width = 40) {
136
+ const filled = Math.floor((percent / 100) * width);
137
+ const empty = width - filled;
138
+
139
+ // Gradient warna berdasarkan progress
140
+ let bar = '';
141
+ for (let i = 0; i < filled; i++) {
142
+ const hue = Math.floor(i / width * 180) + 60; // Cyan to blue
143
+ bar += `\x1b[38;5;${hue}m█\x1b[0m`;
144
+ }
145
+
146
+ for (let i = 0; i < empty; i++) {
147
+ bar += pc.gray('░');
148
+ }
149
+
150
+ return bar;
151
+ }
152
+
153
+ async typewriter(text, speed = 30) {
154
+ process.stdout.write(pc.cyan('▸ ') + pc.gray(''));
155
+ for (let char of text) {
156
+ process.stdout.write(char);
157
+ await new Promise(resolve => setTimeout(resolve, speed));
158
+ }
159
+ console.log();
160
+ }
161
+
162
+ async loadingSequence(steps) {
163
+ console.log('\n');
164
+
165
+ for (let i = 0; i < steps.length; i++) {
166
+ const step = steps[i];
167
+ const isLast = i === steps.length - 1;
168
+
169
+ // Progress bar animation
170
+ for (let p = this.progress; p <= ((i + 1) * 100 / steps.length); p++) {
171
+ this.progress = p;
40
172
 
41
- process.stdout.write('\u001b[5A');
42
- console.log(walkingFrames[frameIndex]);
43
- console.log(`${pc.cyan('▹')} ${pc.white(message)}`);
44
- process.stdout.write(` ${bar} ${pc.cyan(percent + '%')}\r`);
173
+ // Clear previous frame
174
+ this.clearLines(8);
45
175
 
46
- if (i % 3 === 0) frameIndex = frameIndex === 0 ? 1 : 0;
47
- await new Promise(resolve => setTimeout(resolve, 50));
176
+ // Show current step
177
+ const stepText = pc.white(step.title);
178
+ console.log(`${pc.cyan('🛠️ ')} ${stepText}`);
179
+
180
+ // Show animation frame
181
+ const frameIndex = Math.floor(p / 10) % ASCII.walkingFrames.length;
182
+ console.log(ASCII.walkingFrames[frameIndex]);
183
+
184
+ // Show progress bar with percentage
185
+ const bar = this.createProgressBar(p, 50);
186
+ console.log(`\n${bar} ${pc.cyan(p.toFixed(0) + '%')}`);
187
+
188
+ // Show status message with spinner
189
+ const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'][Math.floor(Date.now() / 100) % 10];
190
+ console.log(`${pc.cyan(spinner)} ${pc.gray(step.message)}`);
191
+
192
+ await new Promise(resolve => setTimeout(resolve, step.delay || 50));
193
+ }
194
+
195
+ // Mark step as complete
196
+ this.clearLines(8);
197
+ console.log(`${pc.green('✓')} ${pc.white(step.title)} ${pc.gray('(completed)')}`);
198
+
199
+ if (!isLast) {
200
+ await new Promise(resolve => setTimeout(resolve, 300));
201
+ }
48
202
  }
49
- console.log('\n');
203
+
204
+ console.log();
205
+ }
206
+
207
+ async surfingCelebration() {
208
+ console.log('\n' + ' '.repeat(20) + pc.green('🏄 SURF\'S UP! 🏄') + '\n');
209
+
210
+ for (let i = 0; i < 15; i++) {
211
+ this.clearLines(12);
212
+ const frame = ASCII.surfingFrames[i % ASCII.surfingFrames.length];
213
+ console.log(frame);
214
+
215
+ // Tambahkan efek partikel
216
+ const particles = ['✨', '🌟', '💫', '⭐', '🌊'];
217
+ const particle = particles[Math.floor(Math.random() * particles.length)];
218
+ console.log(' '.repeat(30) + pc.yellow(particle));
219
+
220
+ await new Promise(resolve => setTimeout(resolve, 200));
221
+ }
222
+
223
+ // Final success screen
224
+ this.clearLines(12);
225
+ console.log(ASCII.successArt);
226
+ }
50
227
  }
51
228
 
229
+ // ==================== MAIN APPLICATION ====================
52
230
  async function init() {
53
- process.stdout.write('\x1Bc'); // Clear terminal biar rapi
231
+ const animator = new AnimationEngine();
232
+
233
+ // Clear screen dengan style
234
+ console.log('\x1B[2J\x1B[0f');
235
+
236
+ // Header dengan gradient
237
+ console.log(pc.cyan(`
238
+ ╔═══════════════════════════════════════════════╗
239
+ ║ ║
240
+ ║ ${pc.white('🌊 LETSWHIM CREATOR v' + CONFIG.version)} ║
241
+ ║ ${pc.gray('The Ultralight Web Engine')} ║
242
+ ║ ║
243
+ ╚═══════════════════════════════════════════════╝
244
+ `));
245
+
246
+ console.log();
54
247
 
55
- console.log(`\n${pc.bold(pc.cyan('🌊 LETSWHIM'))} ${pc.gray('v2.0.3')}`);
56
- console.log(`${pc.white('The Ultralight Web Engine for Modern Logic')}\n`);
248
+ // Support npm create letswhim@latest my-project langsung
249
+ let projectName = process.argv[2];
57
250
 
58
- const response = await prompts({
59
- type: 'text',
60
- name: 'projectName',
61
- message: pc.cyan('?') + ' Project name:',
62
- initial: process.argv[2] || 'my-whim-project'
63
- });
251
+ if (!projectName) {
252
+ const response = await prompts({
253
+ type: 'text',
254
+ name: 'projectName',
255
+ message: pc.cyan('?') + ' ' + pc.white('Project name:'),
256
+ initial: 'my-whim-project',
257
+ validate: value => value.trim() === '' ? 'Project name cannot be empty' : true
258
+ });
259
+ projectName = response.projectName?.trim();
260
+ }
64
261
 
65
- const projectName = response.projectName || 'my-whim-project';
262
+ // Fallback jika masih kosong
263
+ projectName = projectName || 'my-whim-project';
66
264
  const targetDir = path.join(process.cwd(), projectName);
67
- const repoUrl = "https://github.com/ripkiiii/letswhim-starter.git";
68
265
 
266
+ // Check if directory exists
69
267
  if (fs.existsSync(targetDir)) {
70
- console.log(`\n${pc.red('✖')} ${pc.bold('Error:')} Directory ${pc.cyan(projectName)} already exists.`);
268
+ console.log(`\n${pc.red('✖')} Directory ${pc.cyan(projectName)} already exists.`);
269
+ console.log(pc.gray('Try a different name or remove the existing directory.'));
71
270
  process.exit(1);
72
271
  }
73
272
 
74
- await startLoading('Preparing your surfboard...');
273
+ // Loading animation sequence
274
+ const loadingSteps = [
275
+ { title: 'Preparing workspace', message: 'Setting up your coding beach...', delay: 80 },
276
+ { title: 'Catching waves', message: 'Cloning the starter template...', delay: 120 },
277
+ { title: 'Waxing the board', message: 'Cleaning up repository...', delay: 60 },
278
+ { title: 'Checking conditions', message: 'Verifying project structure...', delay: 40 },
279
+ { title: 'Final preparations', message: 'Almost ready to surf...', delay: 30 }
280
+ ];
75
281
 
282
+ await animator.loadingSequence(loadingSteps);
283
+
284
+ // Actual processing
76
285
  try {
77
- process.stdout.write(`${pc.cyan('▹')} ${pc.gray('Catching the waves (Cloning template)...')}`);
78
- execSync(`git clone ${repoUrl} "${projectName}"`, { stdio: 'ignore' });
286
+ // Clone repository
287
+ process.stdout.write(`${pc.cyan('⏳')} ${pc.gray('Cloning repository...')}`);
288
+ execSync(`git clone --depth 1 ${CONFIG.repoUrl} "${projectName}"`, { stdio: 'ignore' });
289
+ console.log(`\r${pc.green('✓')} ${pc.white('Repository cloned')}`);
79
290
 
80
- // Hapus .git biar user punya repo bersih
291
+ // Remove .git folder
81
292
  const gitFolder = path.join(targetDir, '.git');
82
293
  if (fs.existsSync(gitFolder)) {
83
- const cmd = process.platform === 'win32' ? `rmdir /s /q "${gitFolder}"` : `rm -rf "${gitFolder}"`;
84
- execSync(cmd);
294
+ fs.rmSync(gitFolder, { recursive: true, force: true });
295
+ console.log(`${pc.green('✓')} ${pc.white('Git history cleared')}`);
296
+ }
297
+
298
+ // Remove unnecessary files
299
+ const readmePath = path.join(targetDir, 'README.md');
300
+ if (fs.existsSync(readmePath)) {
301
+ const newReadme = `# ${projectName}\n\nA LetsWhim project created with create-letswhim@${CONFIG.version}\n\nHappy surfing! 🏄‍♂️`;
302
+ fs.writeFileSync(readmePath, newReadme);
85
303
  }
86
304
 
87
- console.log(`\n\n${pc.bold(pc.cyan('🚀 MISSION ACCOMPLISHED'))}`);
88
- console.log(surfingArt);
305
+ // Celebration animation
306
+ await animator.surfingCelebration();
89
307
 
90
- console.log(`${pc.gray('Project created at')} ${pc.cyan(`./${projectName}`)}`);
308
+ // Success message
309
+ console.log(`\n${pc.green('🎉 SUCCESS!')} ${pc.white('Project created successfully!')}\n`);
91
310
 
92
- console.log(`\n${pc.bold(pc.white('Next steps to surf:'))}`);
93
- console.log(` ${pc.cyan('1.')} cd ${pc.white(projectName)}`);
94
- console.log(` ${pc.cyan('2.')} npm install`);
95
- console.log(` ${pc.cyan('3.')} npm run dev`);
96
-
97
- console.log(`\n${pc.gray("Happy surfing, Developer!")} 🏄‍♂️\n`);
311
+ console.log(pc.cyan('📁 Project location:'));
312
+ console.log(` ${pc.white(`./${projectName}`)}\n`);
313
+
314
+ console.log(pc.cyan('🚀 Next steps to start surfing:'));
315
+ console.log(`${pc.green(' cd')} ${pc.white(projectName)}`);
316
+ console.log(`${pc.green(' npm install')} ${pc.gray('# or pnpm install / yarn')}`);
317
+ console.log(`${pc.green(' npm run dev')} ${pc.gray('# Start development server')}\n`);
318
+
319
+ console.log(pc.cyan('📚 Documentation:'));
320
+ console.log(` ${pc.gray('https://letswhim.pages.dev')}\n`);
321
+
322
+ console.log(pc.gray('💡 Tip: Run ') + pc.white('npm run help') + pc.gray(' to see all available commands'));
323
+
324
+ // Fun ASCII footer
325
+ console.log(pc.blue(`
326
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
327
+ H A P P Y S U R F I N G ! 🏄‍♂️
328
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
329
+ `));
98
330
 
99
331
  } catch (error) {
100
- console.log(`\n${pc.red('✖')} ${pc.bold('Wipeout! (Error):')} ${pc.gray(error.message)}`);
332
+ console.log(`\n${pc.red('💥 WIPEOUT!')} Something went wrong:`);
333
+ console.log(pc.gray(error.message));
334
+
335
+ // Clean up on error
336
+ if (fs.existsSync(targetDir)) {
337
+ try {
338
+ fs.rmSync(targetDir, { recursive: true });
339
+ console.log(pc.gray('\nCleaned up partial installation.'));
340
+ } catch (e) {
341
+ console.log(pc.gray('\nCould not clean up directory.'));
342
+ }
343
+ }
344
+
345
+ process.exit(1);
101
346
  }
102
347
  }
103
348
 
104
- init();
349
+ // Handle Ctrl+C gracefully
350
+ process.on('SIGINT', () => {
351
+ console.log(`\n\n${pc.gray('👋 Catch you on the next wave!')}\n`);
352
+ process.exit(0);
353
+ });
354
+
355
+ // Run the application
356
+ init().catch(error => {
357
+ console.error(`\n${pc.red('💥 UNEXPECTED WIPEOUT!')}`);
358
+ console.error(pc.gray(error.stack || error.message));
359
+ process.exit(1);
360
+ });
package/package.json CHANGED
@@ -1,33 +1,42 @@
1
1
  {
2
2
  "name": "create-letswhim",
3
- "version": "2.0.3",
4
- "description": "The official ultralight CLI to initialize your LetsWhim project in seconds.",
3
+ "version": "2.0.5",
4
+ "description": "The official ultralight CLI to initialize your LetsWhim project with surfing-style animations",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "bin": {
8
- "create-letswhim": "index.js"
8
+ "create-letswhim": "./index.js",
9
+ "create-letswhim-surf": "./index.js"
9
10
  },
10
11
  "keywords": [
11
12
  "letswhim",
12
13
  "ssg",
13
- "l-lang",
14
- "terminal-ui",
15
- "minimalist-framework",
16
- "human-readable",
17
- "ultralight"
14
+ "cli",
15
+ "terminal",
16
+ "animation",
17
+ "surfing",
18
+ "minimalist",
19
+ "framework",
20
+ "create-app"
18
21
  ],
19
22
  "author": "Rifky <rifky@keemail.me>",
20
23
  "license": "MIT",
21
24
  "repository": {
22
25
  "type": "git",
23
- "url": "git+https://github.com/ripkiiii/letswhim-official.git"
26
+ "url": "git+https://github.com/ripkiiii/create-letswhim.git"
24
27
  },
25
28
  "bugs": {
26
- "url": "https://github.com/ripkiiii/letswhim-official/issues"
29
+ "url": "https://github.com/ripkiiii/create-letswhim/"
27
30
  },
28
31
  "homepage": "https://letswhim.pages.dev",
32
+ "engines": {
33
+ "node": ">=14.0.0"
34
+ },
29
35
  "dependencies": {
30
36
  "picocolors": "^1.0.0",
31
37
  "prompts": "^2.4.2"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^20.0.0"
32
41
  }
33
42
  }