create-letswhim 2.0.3 → 2.0.4

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 +318 -68
  2. package/package.json +19 -10
package/index.js CHANGED
@@ -2,103 +2,353 @@
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.4",
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('( )')} ${CONFIG.colors.highlight('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('( )')} ${CONFIG.colors.highlight('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('( )')} ${CONFIG.colors.highlight('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('║')} ${CONFIG.colors.success('✨ PROJECT READY TO SURF! ✨')} ${pc.cyan('║')}`,
107
+ `${pc.cyan('╠═══════════════════════════════════════╣')}`,
108
+ ` `,
109
+ ` ${pc.cyan('\\')} ${pc.cyan('/')} `,
110
+ ` ${pc.cyan('\\')} ${CONFIG.colors.highlight('O')} ${pc.cyan('/')} `,
111
+ ` ${pc.cyan('\\')} ${CONFIG.colors.highlight('K')} ${pc.cyan('/')} `,
112
+ ` ${pc.cyan('╲')}${CONFIG.colors.highlight('!')}${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 += CONFIG.colors.muted('░');
148
+ }
149
+
150
+ return bar;
151
+ }
152
+
153
+ async typewriter(text, speed = 30) {
154
+ process.stdout.write(CONFIG.colors.primary('▸ ') + CONFIG.colors.muted(''));
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 = CONFIG.colors.highlight(step.title);
178
+ console.log(`${CONFIG.colors.primary('🛠️ ')} ${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} ${CONFIG.colors.primary(p.toFixed(0) + '%')}`);
187
+
188
+ // Show status message with spinner
189
+ const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'][Math.floor(Date.now() / 100) % 10];
190
+ console.log(`${CONFIG.colors.primary(spinner)} ${CONFIG.colors.muted(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(`${CONFIG.colors.success('✓')} ${CONFIG.colors.highlight(step.title)} ${CONFIG.colors.muted('(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) + CONFIG.colors.success('🏄 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) + CONFIG.colors.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
54
-
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`);
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(CONFIG.colors.primary(`
238
+ ╔═══════════════════════════════════════════════╗
239
+ ║ ║
240
+ ║ ${CONFIG.colors.highlight('🌊 LETSWHIM CREATOR v' + CONFIG.version)} ║
241
+ ║ ${CONFIG.colors.muted('The Ultralight Web Engine')} ║
242
+ ║ ║
243
+ ╚═══════════════════════════════════════════════╝
244
+ `));
245
+
246
+ console.log();
57
247
 
248
+ // Get project name
58
249
  const response = await prompts({
59
250
  type: 'text',
60
251
  name: 'projectName',
61
- message: pc.cyan('?') + ' Project name:',
62
- initial: process.argv[2] || 'my-whim-project'
252
+ message: CONFIG.colors.primary('?') + ' ' + CONFIG.colors.highlight('Project name:'),
253
+ initial: process.argv[2] || 'my-whim-project',
254
+ validate: value => value.trim() === '' ? 'Project name cannot be empty' : true
63
255
  });
64
256
 
65
- const projectName = response.projectName || 'my-whim-project';
257
+ const projectName = response.projectName?.trim() || 'my-whim-project';
66
258
  const targetDir = path.join(process.cwd(), projectName);
67
- const repoUrl = "https://github.com/ripkiiii/letswhim-starter.git";
68
259
 
260
+ // Check if directory exists
69
261
  if (fs.existsSync(targetDir)) {
70
- console.log(`\n${pc.red('✖')} ${pc.bold('Error:')} Directory ${pc.cyan(projectName)} already exists.`);
262
+ console.log(`\n${CONFIG.colors.error('✖')} Directory ${CONFIG.colors.primary(projectName)} already exists.`);
263
+ console.log(CONFIG.colors.muted('Try a different name or remove the existing directory.'));
71
264
  process.exit(1);
72
265
  }
73
266
 
74
- await startLoading('Preparing your surfboard...');
267
+ // Loading animation sequence
268
+ const loadingSteps = [
269
+ { title: 'Preparing workspace', message: 'Setting up your coding beach...', delay: 80 },
270
+ { title: 'Catching waves', message: 'Cloning the starter template...', delay: 120 },
271
+ { title: 'Waxing the board', message: 'Cleaning up repository...', delay: 60 },
272
+ { title: 'Checking conditions', message: 'Verifying project structure...', delay: 40 },
273
+ { title: 'Final preparations', message: 'Almost ready to surf...', delay: 30 }
274
+ ];
75
275
 
276
+ await animator.loadingSequence(loadingSteps);
277
+
278
+ // Actual processing
76
279
  try {
77
- process.stdout.write(`${pc.cyan('▹')} ${pc.gray('Catching the waves (Cloning template)...')}`);
78
- execSync(`git clone ${repoUrl} "${projectName}"`, { stdio: 'ignore' });
280
+ // Clone repository
281
+ process.stdout.write(`${CONFIG.colors.primary('⏳')} ${CONFIG.colors.muted('Cloning repository...')}`);
282
+ execSync(`git clone --depth 1 ${CONFIG.repoUrl} "${projectName}"`, { stdio: 'ignore' });
283
+ console.log(`\r${CONFIG.colors.success('✓')} ${CONFIG.colors.highlight('Repository cloned')}`);
79
284
 
80
- // Hapus .git biar user punya repo bersih
285
+ // Remove .git folder
81
286
  const gitFolder = path.join(targetDir, '.git');
82
287
  if (fs.existsSync(gitFolder)) {
83
- const cmd = process.platform === 'win32' ? `rmdir /s /q "${gitFolder}"` : `rm -rf "${gitFolder}"`;
84
- execSync(cmd);
288
+ fs.rmSync(gitFolder, { recursive: true, force: true });
289
+ console.log(`${CONFIG.colors.success('✓')} ${CONFIG.colors.highlight('Git history cleared')}`);
290
+ }
291
+
292
+ // Remove unnecessary files
293
+ const readmePath = path.join(targetDir, 'README.md');
294
+ if (fs.existsSync(readmePath)) {
295
+ const newReadme = `# ${projectName}\n\nA LetsWhim project created with create-letswhim@${CONFIG.version}\n\nHappy surfing! 🏄‍♂️`;
296
+ fs.writeFileSync(readmePath, newReadme);
85
297
  }
86
298
 
87
- console.log(`\n\n${pc.bold(pc.cyan('🚀 MISSION ACCOMPLISHED'))}`);
88
- console.log(surfingArt);
299
+ // Celebration animation
300
+ await animator.surfingCelebration();
89
301
 
90
- console.log(`${pc.gray('Project created at')} ${pc.cyan(`./${projectName}`)}`);
302
+ // Success message
303
+ console.log(`\n${CONFIG.colors.success('🎉 SUCCESS!')} ${CONFIG.colors.highlight('Project created successfully!')}\n`);
91
304
 
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`);
305
+ console.log(CONFIG.colors.primary('📁 Project location:'));
306
+ console.log(` ${CONFIG.colors.highlight(`./${projectName}`)}\n`);
307
+
308
+ console.log(CONFIG.colors.primary('🚀 Next steps to start surfing:'));
309
+ console.log(`${CONFIG.colors.success(' cd')} ${CONFIG.colors.highlight(projectName)}`);
310
+ console.log(`${CONFIG.colors.success(' npm install')} ${CONFIG.colors.muted('# or pnpm install / yarn')}`);
311
+ console.log(`${CONFIG.colors.success(' npm run dev')} ${CONFIG.colors.muted('# Start development server')}\n`);
312
+
313
+ console.log(CONFIG.colors.primary('📚 Documentation:'));
314
+ console.log(` ${CONFIG.colors.muted('https://letswhim.pages.dev')}\n`);
315
+
316
+ console.log(CONFIG.colors.muted('💡 Tip: Run ') + CONFIG.colors.highlight('npm run help') + CONFIG.colors.muted(' to see all available commands'));
317
+
318
+ // Fun ASCII footer
319
+ console.log(CONFIG.colors.blue(`
320
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
321
+ H A P P Y S U R F I N G ! 🏄‍♂️
322
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
323
+ `));
98
324
 
99
325
  } catch (error) {
100
- console.log(`\n${pc.red('✖')} ${pc.bold('Wipeout! (Error):')} ${pc.gray(error.message)}`);
326
+ console.log(`\n${CONFIG.colors.error('💥 WIPEOUT!')} Something went wrong:`);
327
+ console.log(CONFIG.colors.muted(error.message));
328
+
329
+ // Clean up on error
330
+ if (fs.existsSync(targetDir)) {
331
+ try {
332
+ fs.rmSync(targetDir, { recursive: true });
333
+ console.log(CONFIG.colors.muted('\nCleaned up partial installation.'));
334
+ } catch (e) {
335
+ console.log(CONFIG.colors.muted('\nCould not clean up directory.'));
336
+ }
337
+ }
338
+
339
+ process.exit(1);
101
340
  }
102
341
  }
103
342
 
104
- init();
343
+ // Handle Ctrl+C gracefully
344
+ process.on('SIGINT', () => {
345
+ console.log(`\n\n${CONFIG.colors.muted('👋 Catch you on the next wave!')}\n`);
346
+ process.exit(0);
347
+ });
348
+
349
+ // Run the application
350
+ init().catch(error => {
351
+ console.error(`\n${CONFIG.colors.error('💥 UNEXPECTED WIPEOUT!')}`);
352
+ console.error(CONFIG.colors.muted(error.stack || error.message));
353
+ process.exit(1);
354
+ });
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.4",
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
  }