create-letswhim 2.0.2 → 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 +313 -75
  2. package/package.json +19 -10
package/index.js CHANGED
@@ -5,112 +5,350 @@ import fs from 'fs';
5
5
  import pc from 'picocolors';
6
6
  import prompts from 'prompts';
7
7
 
8
- // --- FRAME 1: Orang Jalan di Tempat Bawa Papan ---
9
- const walkingFrames = [
10
- ` ${pc.blue(' ( ) ')}
11
- ${pc.blue(' /| |\\ ')}
12
- ${pc.blue(' / \\ ')}
13
- ${pc.cyan('~~~~~~~~~~')}`,
14
- ` ${pc.blue(' ( ) ')}
15
- ${pc.blue(' | |/ ')}
16
- ${pc.blue(' | | ')}
17
- ${pc.cyan('~~~~~~~~~~')}`
18
- ];
19
-
20
- // --- FRAME 2: Orang Menerjang Ombak (Victory) ---
21
- const surfingArt = `
22
- ${pc.blue(' ( ) /')}
23
- ${pc.blue(' /| |/')}
24
- ${pc.blue(' /_|_|/')} ${pc.white("let's just start")}
25
- ${pc.cyan('~')} ${pc.blue(' / \\')}
26
- ${pc.cyan('~~~')} ${pc.blue(' / \\')}
27
- ${pc.cyan('~~~~~')} ${pc.blue(' / \\')}
28
- ${pc.cyan('~~~~~~~~~~~~~~~~~~~~~~~~~~~~')}
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
- const bar = pc.blue('').repeat(i) + pc.gray('░').repeat(total - i);
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;
172
+
173
+ // Clear previous frame
174
+ this.clearLines(8);
39
175
 
40
- // Reset kursor ke atas (5 baris)
41
- process.stdout.write('\u001b[5A');
176
+ // Show current step
177
+ const stepText = CONFIG.colors.highlight(step.title);
178
+ console.log(`${CONFIG.colors.primary('🛠️ ')} ${stepText}`);
42
179
 
43
- // Render Frame Jalan
44
- console.log(walkingFrames[frameIndex]);
180
+ // Show animation frame
181
+ const frameIndex = Math.floor(p / 10) % ASCII.walkingFrames.length;
182
+ console.log(ASCII.walkingFrames[frameIndex]);
45
183
 
46
- // Render Progress Bar
47
- console.log(`${pc.cyan('▹')} ${pc.white(message)}`);
48
- process.stdout.write(` ${bar} ${pc.blue(percent + '%')}\r`);
184
+ // Show progress bar with percentage
185
+ const bar = this.createProgressBar(p, 50);
186
+ console.log(`\n${bar} ${CONFIG.colors.primary(p.toFixed(0) + '%')}`);
49
187
 
50
- if (i % 3 === 0) frameIndex = frameIndex === 0 ? 1 : 0;
51
- await new Promise(resolve => setTimeout(resolve, 50));
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
+ }
52
202
  }
53
- 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
+ }
54
227
  }
55
228
 
229
+ // ==================== MAIN APPLICATION ====================
56
230
  async function init() {
57
- // Clear Terminal Biar Aesthetic
58
- process.stdout.write('\x1Bc');
59
-
60
- console.log(`\n${pc.bold(pc.blue('🌊 LETSWHIM'))} ${pc.gray('v2.0.2')}`);
61
- 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();
62
247
 
248
+ // Get project name
63
249
  const response = await prompts({
64
250
  type: 'text',
65
251
  name: 'projectName',
66
- message: pc.cyan('?') + ' Project name:',
67
- 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
68
255
  });
69
256
 
70
- const projectName = response.projectName || 'my-whim-project';
257
+ const projectName = response.projectName?.trim() || 'my-whim-project';
71
258
  const targetDir = path.join(process.cwd(), projectName);
72
- const repoUrl = "https://github.com/ripkiiii/letswhim-starter.git";
73
259
 
260
+ // Check if directory exists
74
261
  if (fs.existsSync(targetDir)) {
75
- 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.'));
76
264
  process.exit(1);
77
265
  }
78
266
 
79
- // 1. ANIMASI JALAN DI TEMPAT
80
- 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
+ ];
275
+
276
+ await animator.loadingSequence(loadingSteps);
81
277
 
278
+ // Actual processing
82
279
  try {
83
- // 2. PROSES CLONE
84
- process.stdout.write(`${pc.blue('')} ${pc.gray('Catching the waves (Cloning template)...')}`);
85
- 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')}`);
86
284
 
87
- // 3. PAKSA HAPUS .GIT (Anti ENOTEMPTY)
285
+ // Remove .git folder
88
286
  const gitFolder = path.join(targetDir, '.git');
89
287
  if (fs.existsSync(gitFolder)) {
90
- try {
91
- fs.rmSync(gitFolder, { recursive: true, force: true });
92
- } catch (e) {
93
- const cmd = process.platform === 'win32' ? `rmdir /s /q "${gitFolder}"` : `rm -rf "${gitFolder}"`;
94
- execSync(cmd);
95
- }
288
+ fs.rmSync(gitFolder, { recursive: true, force: true });
289
+ console.log(`${CONFIG.colors.success('✓')} ${CONFIG.colors.highlight('Git history cleared')}`);
96
290
  }
97
291
 
98
- // 4. DISPLAY SURFER MENERJANG OMBAK
99
- console.log(`\n\n${pc.bold(pc.blue('🚀 MISSION ACCOMPLISHED'))}`);
100
- console.log(surfingArt);
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);
297
+ }
101
298
 
102
- console.log(`${pc.gray('Project created at')} ${pc.cyan(`./${projectName}`)}`);
103
-
104
- console.log(`\n${pc.bold(pc.white('Next steps to surf:'))}`);
105
- console.log(` ${pc.blue('1.')} cd ${pc.white(projectName)}`);
106
- console.log(` ${pc.blue('2.')} npm install`);
107
- console.log(` ${pc.blue('3.')} npm run dev`);
299
+ // Celebration animation
300
+ await animator.surfingCelebration();
108
301
 
109
- console.log(`\n${pc.gray("Happy surfing, Developer!")} 🏄‍♂️\n`);
302
+ // Success message
303
+ console.log(`\n${CONFIG.colors.success('🎉 SUCCESS!')} ${CONFIG.colors.highlight('Project created successfully!')}\n`);
304
+
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
+ `));
110
324
 
111
325
  } catch (error) {
112
- 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);
113
340
  }
114
341
  }
115
342
 
116
- 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.2",
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
  }