create-laju-app 1.0.14 → 1.1.1

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 (3) hide show
  1. package/README.md +149 -309
  2. package/bin/cli.js +237 -120
  3. package/package.json +28 -28
package/README.md CHANGED
@@ -1,309 +1,149 @@
1
- # Laju
2
-
3
- A high-performance TypeScript web framework combining HyperExpress, Svelte 5, and Inertia.js for building modern full-stack applications. Features server-side rendering, real-time capabilities, and seamless client-server state management.
4
-
5
- Visit [laju.dev](https://laju.dev)
6
-
7
- ## Features
8
-
9
- - Fast server-side rendering with HyperExpress
10
- - Modern frontend with Svelte 5
11
- - TypeScript support for better type safety
12
- - Inertia.js integration for seamless client-server communication
13
- - Built-in authentication system
14
- - SQLite database with Knex query builder
15
- - Email support with Nodemailer
16
- - Google APIs integration
17
- - Redis caching support
18
- - Asset bundling with Vite
19
- - TailwindCSS for styling
20
-
21
- ## Prerequisites
22
-
23
- - Node.js (Latest LTS version recommended)
24
- - npm or yarn
25
- - Redis server (optional, for caching)
26
-
27
- ## Installation
28
-
29
- Run the following command
30
- ```bash
31
- npx create-laju-app project-name
32
- ```
33
- ## Development
34
-
35
- To start the development server:
36
-
37
- ```bash
38
- npm run dev
39
- ```
40
-
41
- This will:
42
- - Start the Vite development server for frontend assets
43
- - Run the backend server with nodemon for auto-reloading
44
-
45
- ## Building for Production
46
-
47
- To build the application for production:
48
-
49
- ```bash
50
- npm run build
51
- ```
52
-
53
- This command will:
54
- - Clean the build directory
55
- - Build frontend assets with Vite
56
- - Compile TypeScript files
57
- - Copy necessary files to the build directory
58
-
59
- ## Project Structure
60
-
61
- - `/app` - Core application files
62
- - `/middlewares` - Custom middleware functions
63
- - `/services` - Service layer implementations
64
- - `/controllers` - Application controllers
65
- - `/resources` - Frontend resources
66
- - `/views` - Svelte components and views
67
- - `/js` - JavaScript assets and modules
68
- - `/routes` - Route definitions
69
- - `/migrations` - Database migrations
70
- - `/public` - Static files
71
- - `/dist` - Compiled assets (generated)
72
- - `/build` - Production build output
73
-
74
- ## Key Dependencies
75
-
76
- ### Backend
77
- - HyperExpress - High-performance web server
78
- - Knex - SQL query builder
79
- - SQLite3 - Database
80
- - Nodemailer - Email sending
81
- - Redis - Caching (optional)
82
-
83
- ### Frontend
84
- - Svelte 5 - UI framework
85
- - Inertia.js - Client-server communication
86
- - TailwindCSS - Utility-first CSS framework
87
- - Vite - Build tool and dev server
88
-
89
- ## Scripts
90
-
91
- - `npm run dev` - Start development server
92
- - `npm run build` - Build for production
93
-
94
- ## Contributing
95
-
96
- 1. Fork the repository
97
- 2. Create your feature branch
98
- 3. Commit your changes
99
- 4. Push to the branch
100
- 5. Create a new Pull Request
101
-
102
- ## License
103
-
104
- ISC License
105
-
106
- ## Tutorial: Building Your First App
107
-
108
- This tutorial will guide you through building a simple application using this framework.
109
-
110
- ### 1. Setting Up a New Route and Controller
111
-
112
- First, let's create a new route and controller for a blog post feature.
113
-
114
- 1. Create a new controller file `app/controllers/PostController.ts`:
115
-
116
- ```typescript
117
- import { Request, Response } from "../../type";
118
- import DB from "../services/DB";
119
-
120
- class Controller {
121
- public async index(request: Request, response: Response) {
122
- const posts = await DB.from("posts");
123
- return response.inertia("posts/index", { posts });
124
- }
125
-
126
- public async create(request: Request, response: Response) {
127
- return response.inertia("posts/create");
128
- }
129
-
130
- public async store(request: Request, response: Response) {
131
- const { title, content } = request.body;
132
-
133
- await DB.table("posts").insert({
134
- title,
135
- content,
136
- created_at: Date.now(),
137
- updated_at: Date.now()
138
- });
139
-
140
- return response.redirect("/posts");
141
- }
142
- }
143
-
144
- export default new Controller();
145
- ```
146
-
147
- 2. Add routes in `routes/web.ts`:
148
-
149
- ```typescript
150
- import PostController from "../app/controllers/PostController";
151
-
152
- // Add these routes with your existing routes
153
- Route.get("/posts", PostController.index);
154
- Route.get("/posts/create", PostController.create);
155
- Route.post("/posts", PostController.store);
156
- ```
157
-
158
- ### 2. Creating the Database Migration
159
-
160
- Create a migration for the posts table:
161
-
162
- ```bash
163
- npx knex migrate:make create_posts_table
164
- ```
165
-
166
- In the generated migration file:
167
-
168
- ```typescript
169
- import { Knex } from "knex";
170
-
171
- export async function up(knex: Knex): Promise<void> {
172
- await knex.schema.createTable('posts', function (table) {
173
- table.increments('id').primary();
174
- table.string('title').notNullable();
175
- table.text('content').notNullable();
176
- table.bigInteger('created_at');
177
- table.bigInteger('updated_at');
178
- });
179
- }
180
-
181
- export async function down(knex: Knex): Promise<void> {
182
- await knex.schema.dropTable('posts');
183
- }
184
- ```
185
-
186
- Run the migration:
187
-
188
- ```bash
189
- npx knex migrate:latest
190
- ```
191
-
192
- ### 3. Creating Svelte Components
193
-
194
- 1. Create `resources/views/posts/index.svelte`:
195
-
196
- ```svelte
197
- <script>
198
- export let posts = [];
199
- </script>
200
-
201
- <div class="max-w-4xl mx-auto p-4">
202
- <div class="flex justify-between items-center mb-6">
203
- <h1 class="text-2xl font-bold">Blog Posts</h1>
204
- <a
205
- href="/posts/create"
206
- class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
207
- >
208
- Create Post
209
- </a>
210
- </div>
211
-
212
- <div class="space-y-4">
213
- {#each posts as post}
214
- <div class="border p-4 rounded">
215
- <h2 class="text-xl font-semibold">{post.title}</h2>
216
- <p class="mt-2 text-gray-600">{post.content}</p>
217
- </div>
218
- {/each}
219
- </div>
220
- </div>
221
- ```
222
-
223
- 2. Create `resources/views/posts/create.svelte`:
224
-
225
- ```svelte
226
- <script>
227
- import { router } from '@inertiajs/svelte';
228
-
229
- let form = {
230
- title: '',
231
- content: ''
232
- };
233
-
234
- function handleSubmit() {
235
- router.post('/posts', form);
236
- }
237
- </script>
238
-
239
- <div class="max-w-4xl mx-auto p-4">
240
- <h1 class="text-2xl font-bold mb-6">Create New Post</h1>
241
-
242
- <form on:submit|preventDefault={handleSubmit} class="space-y-4">
243
- <div>
244
- <label class="block text-sm font-medium mb-1">Title</label>
245
- <input
246
- type="text"
247
- bind:value={form.title}
248
- class="w-full px-3 py-2 border rounded"
249
- />
250
- </div>
251
-
252
- <div>
253
- <label class="block text-sm font-medium mb-1">Content</label>
254
- <textarea
255
- bind:value={form.content}
256
- class="w-full px-3 py-2 border rounded h-32"
257
- ></textarea>
258
- </div>
259
-
260
- <div>
261
- <button
262
- type="submit"
263
- class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
264
- >
265
- Create Post
266
- </button>
267
- </div>
268
- </form>
269
- </div>
270
- ```
271
-
272
- ### 4. Testing Your Application
273
-
274
- 1. Start the development server:
275
- ```bash
276
- npm run dev
277
- ```
278
-
279
- 2. Visit `http://localhost:5555/posts` in your browser
280
- 3. Try creating a new post using the form
281
- 4. View the list of posts on the index page
282
-
283
- ### Key Concepts
284
-
285
- 1. **Routing**: Routes are defined in `routes/web.ts` using the HyperExpress router
286
- 2. **Controllers**: Handle business logic and return Inertia responses
287
- 3. **Database**: Use Knex.js for database operations and migrations
288
- 4. **Frontend**: Svelte components with Inertia.js for seamless page transitions
289
- 5. **Styling**: TailwindCSS for utility-first styling
290
-
291
- ### Best Practices
292
-
293
- 1. **File Organization**
294
- - Keep controllers in `app/controllers`
295
- - Place Svelte components in `resources/views`
296
- - Database migrations in `migrations`
297
-
298
- 2. **Code Structure**
299
- - Use TypeScript types for better type safety
300
- - Keep controllers focused on single responsibilities
301
- - Use Inertia.js for state management between server and client
302
-
303
- 3. **Database**
304
- - Always use migrations for database changes
305
- - Use the Query Builder for complex queries
306
- - Include timestamps for tracking record changes
307
-
308
- Need help with anything specific? Feel free to ask!
309
-
1
+ # Laju
2
+
3
+ **High-performance TypeScript web framework** - 11x faster than Express.js
4
+
5
+ Build modern full-stack applications with HyperExpress, Svelte 5, and Inertia.js.
6
+
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+ [![Node.js Version](https://img.shields.io/badge/node-20--22-brightgreen)](https://nodejs.org)
9
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue?logo=typescript)](https://www.typescriptlang.org/)
10
+ [![GitHub stars](https://img.shields.io/github/stars/maulanashalihin/laju?style=social)](https://github.com/maulanashalihin/laju)
11
+
12
+ ## 🚀 Quick Start
13
+
14
+ ```bash
15
+ # Create new project
16
+ npx create-laju-app my-project
17
+ cd my-project
18
+
19
+ # Setup database
20
+ cp .env.example .env
21
+ npx knex migrate:latest
22
+
23
+ # Start development
24
+ npm run dev
25
+ ```
26
+
27
+ Visit `http://localhost:5555`
28
+
29
+ ## Features
30
+
31
+ ### Performance First
32
+ - **258,611 req/sec** - HyperExpress server (11x faster than Express)
33
+ - **19.9x faster writes** - SQLite with WAL mode
34
+ - **Zero-config caching** - Database cache included (optional Redis)
35
+
36
+ ### Modern Stack
37
+ - **Svelte 5** - Reactive UI with runes
38
+ - **Inertia.js** - SPA without client-side routing
39
+ - **TailwindCSS 4** - Utility-first CSS with Vite
40
+ - **TypeScript** - Full type safety
41
+
42
+ ### Built-in Services
43
+ - **Authentication** - Sessions, OAuth (Google), password reset
44
+ - **Storage** - S3/Wasabi with presigned URLs
45
+ - **Email** - Nodemailer (SMTP) or Resend (API)
46
+ - **Caching** - Database cache or Redis
47
+ - **Templates** - Eta for SSR
48
+
49
+ ## 📊 Performance
50
+
51
+ | Framework | Requests/sec | Comparison |
52
+ |-----------|--------------|------------|
53
+ | **Laju** | **258,611** | Baseline |
54
+ | Pure Node.js | 124,024 | 2x slower |
55
+ | Express.js | 22,590 | 11x slower |
56
+ | Laravel | 80 | 3,232x slower |
57
+
58
+ *Benchmark: Simple JSON response on same hardware*
59
+
60
+ ## 📚 Documentation
61
+
62
+ **[Complete Documentation →](docs/README.md)**
63
+
64
+ Documentation is organized for progressive learning from beginner to advanced.
65
+
66
+ ### Getting Started
67
+ - [Introduction](docs/01-INTRODUCTION.md) - Framework overview, quick start
68
+ - [Project Structure](docs/02-PROJECT-STRUCTURE.md) - Directory layout
69
+ - [Database](docs/03-DATABASE.md) - Knex.js + SQLite
70
+
71
+ ### Core Features
72
+ - [Routing & Controllers](docs/04-ROUTING-CONTROLLERS.md) - Handle requests
73
+ - [Frontend (Svelte 5)](docs/05-FRONTEND-SVELTE.md) - Build reactive UI
74
+ - [Authentication](docs/06-AUTHENTICATION.md) - Sessions + OAuth
75
+ - [Middleware](docs/07-MIDDLEWARE.md) - Auth, rate limiting
76
+ - [Validation](docs/08-VALIDATION.md) - Input validation
77
+ - [Email](docs/09-EMAIL.md) - Send emails
78
+
79
+ ### Advanced Features
80
+ - [Storage (S3)](docs/10-STORAGE.md) - File uploads
81
+ - [Caching](docs/11-CACHING.md) - Redis + Database cache
82
+ - [Background Jobs](docs/12-BACKGROUND-JOBS.md) - Cron jobs, Scheduling
83
+ - [CSRF Protection](docs/13-CSRF.md) - Security
84
+ - [Translation](docs/14-TRANSLATION.md) - Multi-language
85
+
86
+ ### Production
87
+ - [Best Practices](docs/16-BEST-PRACTICES.md) - Code quality
88
+ - [Security Guide](docs/17-SECURITY.md) - Secure your app
89
+ - [Testing](docs/19-TESTING.md) - Unit + Integration tests
90
+ - [Deployment](docs/20-DEPLOYMENT.md) - Production setup
91
+
92
+ ## Project Structure
93
+
94
+ ```
95
+ app/
96
+ ├── controllers/ # Request handlers
97
+ ├── middlewares/ # Auth, rate limiting
98
+ ├── services/ # DB, Mailer, Storage
99
+ └── validators/ # Input validation
100
+
101
+ resources/
102
+ ├── js/
103
+ │ ├── Pages/ # Svelte/Inertia pages
104
+ │ ├── Components/ # Reusable components
105
+ │ └── index.css # TailwindCSS 4
106
+ └── views/ # Eta templates
107
+
108
+ routes/ # Route definitions
109
+ migrations/ # Database migrations
110
+ ```
111
+
112
+ ## Commands
113
+
114
+ ```bash
115
+ npm run dev # Development
116
+ npm run build # Production build
117
+ node laju make:controller UserController # Generate controller
118
+ npx knex migrate:make create_posts # Create migration
119
+ npx knex migrate:latest # Run migrations
120
+ ```
121
+
122
+ ## Tech Stack
123
+
124
+ | Layer | Technology |
125
+ |-------|------------|
126
+ | Server | HyperExpress |
127
+ | Database | BetterSQLite3 + Knex |
128
+ | Frontend | Svelte 5 + Inertia.js |
129
+ | Styling | TailwindCSS 4 |
130
+ | Build | Vite |
131
+ | Templates | Eta |
132
+
133
+ ## Author
134
+
135
+ **Maulana Shalihin** - [maulana@drip.id](mailto:maulana@drip.id)
136
+
137
+ - [tapsite.ai](https://tapsite.ai) - AI website builder
138
+ - [dripsender.id](https://dripsender.id) - Email marketing
139
+ - [laju.dev](https://laju.dev) - This framework
140
+
141
+ ## Support
142
+
143
+ - Star this repository
144
+ - [Become a Sponsor](https://github.com/sponsors/maulanashalihin)
145
+ - Report bugs via [GitHub Issues](https://github.com/maulanashalihin/laju/issues)
146
+
147
+ ## License
148
+
149
+ MIT License
package/bin/cli.js CHANGED
@@ -1,121 +1,238 @@
1
- #!/usr/bin/env node
2
-
3
- const { program } = require('commander');
4
- const prompts = require('prompts');
5
- const degit = require('degit');
6
- const path = require('path');
7
- const fs = require('fs');
8
- const { execSync } = require('child_process');
9
-
10
- const ASCII_ART = ` -
11
- :+===+ =+
12
- ++++++++++++++
13
- =++++= =+++
14
- +++= +++=
15
- ++= +++++
16
- ++ ++++++ +
17
- == =++++++++++++ -=
18
- = =++++++++++++ ++
19
- = ++++++ ++
20
- - =++++ =+=
21
- =++++ +++
22
- =++= -+++=
23
- ++++++++++++++=
24
- ++=+++++++++=
25
- == `;
26
-
27
- program
28
- .name('create-laju-app')
29
- .description('CLI to create a new project from template')
30
- .version('1.0.0');
31
-
32
- program
33
- .argument('[project-directory]', 'Project directory name')
34
- .action(async (projectDirectory) => {
35
- try {
36
- console.log(ASCII_ART); // Tambahkan ini
37
- console.log('\n'); // Tambah baris kosong setelah ASCII art
38
- // If no project name, ask user
39
- if (!projectDirectory) {
40
- const response = await prompts({
41
- type: 'text',
42
- name: 'projectDirectory',
43
- message: 'Enter project name:'
44
- });
45
- projectDirectory = response.projectDirectory;
46
- }
47
-
48
- if (!projectDirectory) {
49
- console.log('Project name is required to continue.');
50
- process.exit(1);
51
- }
52
-
53
- const targetPath = path.resolve(projectDirectory);
54
-
55
- // Check if directory exists
56
- if (fs.existsSync(targetPath)) {
57
- console.log(`Directory ${projectDirectory} already exists. Choose another name.`);
58
- process.exit(1);
59
- }
60
-
61
- console.log(`Creating a new project in ${targetPath}...`);
62
-
63
- // Clone template from GitHub
64
- const emitter = degit('maulanashalihin/laju');
65
-
66
- await emitter.clone(targetPath);
67
-
68
- // Read package.json from template
69
- const packageJsonPath = path.join(targetPath, 'package.json');
70
- const packageJson = require(packageJsonPath);
71
-
72
- // Update project name in package.json
73
- packageJson.name = projectDirectory;
74
-
75
- // Write back package.json
76
- fs.writeFileSync(
77
- packageJsonPath,
78
- JSON.stringify(packageJson, null, 2)
79
- );
80
-
81
- // Change directory and run setup commands
82
- process.chdir(targetPath);
83
- console.log('📦 Installing dependencies...');
84
- execSync('npm install', { stdio: 'inherit' });
85
- console.log('📝 Copying environment file...');
86
-
87
- execSync(process.platform === 'win32' ? 'copy .env.example .env' : 'cp .env.example .env', { stdio: 'inherit' });
88
- console.log('🔄 Running migrations...');
89
- execSync('npx knex migrate:latest', { stdio: 'inherit' });
90
- // Update scripts in package.json for Windows
91
- if (process.platform === 'win32') {
92
- packageJson.scripts = {
93
- "dev": "cls && npx concurrently \"vite\" \"npx nodemon\"",
94
- "build": "if exist build rmdir /s /q build && vite build && tsc && xcopy /s /e /i dist build && xcopy /s /e /i public build"
95
- };
96
-
97
- // Write updated package.json
98
- fs.writeFileSync(
99
- packageJsonPath,
100
- JSON.stringify(packageJson, null, 2)
101
- );
102
- }
103
-
104
- console.log('🎉 Project created successfully!');
105
- console.log('');
106
- console.log('🚀 Your project is ready! You can now start developing.');
107
-
108
- console.log('');
109
- console.log('👉 Next steps:');
110
- console.log('1. 📁 cd ' + projectDirectory);
111
- console.log('2. 🔥 npm run dev => to start the development server.');
112
- console.log('3. 📦 npm run build => to build the production files.');
113
- console.log('');
114
-
115
- } catch (error) {
116
- console.error('Error:', error.message);
117
- process.exit(1);
118
- }
119
- });
120
-
1
+ #!/usr/bin/env node
2
+
3
+ const { program } = require('commander');
4
+ const prompts = require('prompts');
5
+ const degit = require('degit');
6
+ const path = require('path');
7
+ const fs = require('fs');
8
+ const { execSync } = require('child_process');
9
+
10
+ function detectAvailablePackageManagers() {
11
+ const { execSync } = require('child_process');
12
+ const packageManagers = ['bun', 'yarn', 'npm'];
13
+ const available = [];
14
+
15
+ for (const pm of packageManagers) {
16
+ try {
17
+ execSync(`${pm} --version`, { stdio: 'ignore' });
18
+ available.push(pm);
19
+ } catch (e) {
20
+ continue;
21
+ }
22
+ }
23
+ return available.length > 0 ? available : ['npm'];
24
+ }
25
+
26
+ async function selectPackageManager(available) {
27
+ if (available.length === 1) {
28
+ return available[0];
29
+ }
30
+
31
+ console.log('');
32
+ console.log('\x1b[36m Which package manager would you like to use?\x1b[0m');
33
+
34
+ const response = await prompts({
35
+ type: 'select',
36
+ name: 'packageManager',
37
+ message: '',
38
+ choices: available.map(pm => ({
39
+ title: pm === 'npm' ? 'npm (stable ✓)' : pm === 'yarn' ? 'yarn (fast ✓)' : 'bun (experimental ⚠️)',
40
+ value: pm
41
+ }))
42
+ });
43
+
44
+ const selected = response.packageManager || 'npm';
45
+
46
+ if (selected === 'bun') {
47
+ console.log('\x1b[90m Note: Bun is experimental. If you encounter issues, try npm or yarn.\x1b[0m');
48
+ console.log('');
49
+ }
50
+
51
+ return selected;
52
+ }
53
+
54
+ function getInstallCommand(packageManager) {
55
+ switch (packageManager) {
56
+ case 'bun':
57
+ return 'bun install';
58
+ case 'yarn':
59
+ return 'yarn';
60
+ case 'npm':
61
+ default:
62
+ return 'npm install';
63
+ }
64
+ }
65
+
66
+ function getRunCommand(packageManager) {
67
+ switch (packageManager) {
68
+ case 'bun':
69
+ return 'bun run';
70
+ case 'yarn':
71
+ return 'yarn';
72
+ case 'npm':
73
+ default:
74
+ return 'npm run';
75
+ }
76
+ }
77
+
78
+ const ASCII_ART = `
79
+ \x1b[38;5;208m
80
+ ++++++++++++++++++
81
+ +++++++++++++++++++
82
+ +++++++++++++++++++
83
+ +++++++++++++++++++
84
+ +++++++++++++++++++
85
+ ++++++++++++++++++++
86
+ +++++++++++++++++++
87
+ +++++++++++++++++++
88
+ +++++++++++++++++++
89
+ +++++++++++++++++++
90
+ +++++++++++++++++++
91
+
92
+ +++++++++++++++++++++++++++++++++
93
+ ++++++++++++++++++++++++++++++++++
94
+ +++++++++++++++++++++++++++++++++
95
+ ++++++++++++++++++++++++++++++++++
96
+ ++++++++++++++++++++++++++++++++++
97
+ ++++++++++++++++++++++++++++++++++
98
+ +++++++++++++++++++++++++++++++++
99
+ +++++++++++++++++++++++++++++++++
100
+ ++++++++++++++++++++++++++++++++
101
+ \x1b[0m
102
+ `;
103
+
104
+ program
105
+ .name('create-laju-app')
106
+ .description('CLI to create a new project from template')
107
+ .version('1.0.0');
108
+
109
+ program
110
+ .argument('[project-directory]', 'Project directory name')
111
+ .option('--package-manager <pm>', 'Package manager to use (npm, yarn, bun)')
112
+ .action(async (projectDirectory, options) => {
113
+ try {
114
+ console.log('');
115
+ console.log(ASCII_ART);
116
+ console.log('\x1b[36m Create a new project with Laju Framework\x1b[0m');
117
+ console.log('');
118
+
119
+ let packageManager;
120
+ if (options.packageManager) {
121
+ packageManager = options.packageManager;
122
+ if (!['npm', 'yarn', 'bun'].includes(packageManager)) {
123
+ console.log('\x1b[1;31m✖\x1b[0m \x1b[1;91mError:\x1b[0m Invalid package manager. Use npm, yarn, or bun.');
124
+ process.exit(1);
125
+ }
126
+ } else {
127
+ const availablePackageManagers = detectAvailablePackageManagers();
128
+ packageManager = await selectPackageManager(availablePackageManagers);
129
+ }
130
+ console.log('\x1b[36m Using ' + packageManager + '\x1b[0m');
131
+ console.log('');
132
+
133
+ // If no project name, ask user
134
+ if (!projectDirectory) {
135
+ console.log('\x1b[36m Project name:\x1b[0m');
136
+ const response = await prompts({
137
+ type: 'text',
138
+ name: 'projectDirectory',
139
+ message: ''
140
+ });
141
+ projectDirectory = response.projectDirectory;
142
+ }
143
+
144
+ if (!projectDirectory) {
145
+ console.log('\x1b[1;31m✖\x1b[0m \x1b[1;91mError:\x1b[0m Project name is required to continue.');
146
+ process.exit(1);
147
+ }
148
+
149
+ // Validate project name (npm package name rules)
150
+ const nameRegex = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
151
+ if (!nameRegex.test(projectDirectory)) {
152
+ console.log('\x1b[1;31m✖\x1b[0m \x1b[1;91mError:\x1b[0m Invalid project name. Use lowercase letters, numbers, and hyphens only.');
153
+ process.exit(1);
154
+ }
155
+
156
+ const targetPath = path.resolve(projectDirectory);
157
+
158
+ // Check if directory exists
159
+ if (fs.existsSync(targetPath)) {
160
+ console.log('\x1b[1;31m✖\x1b[0m \x1b[1;91mError:\x1b[0m Directory \x1b[36m' + projectDirectory + '\x1b[0m already exists. Choose another name.');
161
+ process.exit(1);
162
+ }
163
+
164
+ console.log('');
165
+ console.log('\x1b[90m Creating project at \x1b[36m' + targetPath + '\x1b[0m');
166
+ console.log('');
167
+
168
+ // Clone template from GitHub
169
+ const emitter = degit('maulanashalihin/laju');
170
+
171
+ await emitter.clone(targetPath);
172
+
173
+ // Read package.json from template
174
+ const packageJsonPath = path.join(targetPath, 'package.json');
175
+ const packageJson = require(packageJsonPath);
176
+
177
+ // Update project name in package.json
178
+ packageJson.name = projectDirectory;
179
+
180
+ // Update scripts for Windows before writing package.json
181
+ if (process.platform === 'win32') {
182
+ packageJson.scripts = {
183
+ "dev": "cls && npx concurrently \"vite\" \"npx nodemon\"",
184
+ "build": "if exist build rmdir /s /q build && vite build && tsc && xcopy /s /e /i dist build && xcopy /s /e /i public build"
185
+ };
186
+ }
187
+
188
+ // Write back package.json
189
+ fs.writeFileSync(
190
+ packageJsonPath,
191
+ JSON.stringify(packageJson, null, 2)
192
+ );
193
+
194
+ // Change directory and run setup commands
195
+ const originalDir = process.cwd();
196
+ process.chdir(targetPath);
197
+
198
+ try {
199
+ console.log('\x1b[36m Installing dependencies...\x1b[0m');
200
+ console.log('');
201
+ execSync(getInstallCommand(packageManager), { stdio: 'inherit', timeout: 300000 });
202
+ console.log('\x1b[32m ✓ Dependencies installed\x1b[0m');
203
+ console.log('');
204
+
205
+ console.log('\x1b[36m Setting up environment...\x1b[0m');
206
+ execSync(process.platform === 'win32' ? 'copy .env.example .env' : 'cp .env.example .env', { stdio: 'inherit', timeout: 10000 });
207
+ console.log('\x1b[32m ✓ Environment configured\x1b[0m');
208
+ console.log('');
209
+
210
+ console.log('\x1b[36m Preparing database...\x1b[0m');
211
+ execSync('npx knex migrate:latest', { stdio: 'inherit', timeout: 60000 });
212
+ console.log('\x1b[32m ✓ Database ready\x1b[0m');
213
+ } finally {
214
+ process.chdir(originalDir);
215
+ }
216
+
217
+ console.log('');
218
+ console.log('\x1b[1;36m ✓ Project created successfully!\x1b[0m');
219
+ console.log('');
220
+ console.log('\x1b[90m Next steps:\x1b[0m');
221
+ console.log('');
222
+ console.log(' cd ' + projectDirectory);
223
+ console.log(' ' + getRunCommand(packageManager) + ' dev');
224
+ console.log('');
225
+ console.log('\x1b[90m Learn more: https://laju.dev\x1b[0m');
226
+ console.log('\x1b[90m Docs: https://github.com/maulanashalihin/laju/tree/main/docs\x1b[0m');
227
+ console.log('');
228
+
229
+ } catch (error) {
230
+ console.error('Error:', error.message);
231
+ if (process.env.DEBUG) {
232
+ console.error(error.stack);
233
+ }
234
+ process.exit(1);
235
+ }
236
+ });
237
+
121
238
  program.parse();
package/package.json CHANGED
@@ -1,28 +1,28 @@
1
- {
2
- "name": "create-laju-app",
3
- "version": "1.0.14",
4
- "keywords": [
5
- "laju",
6
- "svelte",
7
- "tailwindcss",
8
- "hyper-express",
9
- "sqlite",
10
- "boilerplate",
11
- "template",
12
- "generator"
13
- ],
14
- "bin": {
15
- "create-laju-app": "./bin/cli.js"
16
- },
17
- "dependencies": {
18
- "child_process": "^1.0.2",
19
- "commander": "^11.0.0",
20
- "degit": "^2.8.4",
21
- "prompts": "^2.4.2"
22
- },
23
- "homepage": "https://laju.dev",
24
- "repository": {
25
- "type": "git",
26
- "url": "git+https://github.com/maulanashalihin/laju.git"
27
- }
28
- }
1
+ {
2
+ "name": "create-laju-app",
3
+ "version": "1.1.1",
4
+ "keywords": [
5
+ "laju",
6
+ "svelte",
7
+ "tailwindcss",
8
+ "hyper-express",
9
+ "sqlite",
10
+ "boilerplate",
11
+ "template",
12
+ "generator"
13
+ ],
14
+ "bin": {
15
+ "create-laju-app": "./bin/cli.js"
16
+ },
17
+ "dependencies": {
18
+ "child_process": "^1.0.2",
19
+ "commander": "^11.0.0",
20
+ "degit": "^2.8.4",
21
+ "prompts": "^2.4.2"
22
+ },
23
+ "homepage": "https://laju.dev",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/maulanashalihin/laju.git"
27
+ }
28
+ }