create-tauri-app-project-wind 1.0.2 → 1.1.0

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 +32 -13
  2. package/index.js +270 -22
  3. package/package.json +18 -5
package/README.md CHANGED
@@ -4,31 +4,50 @@ A modern, professional desktop application starter template built with Tauri, Re
4
4
 
5
5
  ## šŸš€ Quick Start
6
6
 
7
- ### npm
7
+ ### Interactive Installation (Recommended)
8
+
9
+ The CLI will guide you through configuration with interactive prompts:
10
+
11
+ ```bash
12
+ npm create tauri-app-project-wind@latest
13
+ ```
14
+
15
+ You'll be prompted for:
16
+ - šŸ“ **Project folder name** – Name for your project directory (lowercase, dashes/underscores allowed)
17
+ - šŸ·ļø **App display name** – How your app will be labeled in the window/titlebar
18
+ - šŸ‘¤ **Author name** – Your name or organization (used in metadata)
19
+ - šŸ“¦ **Bundle identifier** – Reverse domain (e.g., com.company.app) for Tauri packaging
20
+ - šŸ“¦ **Package manager** – Choose npm, yarn, pnpm, or bun
21
+ - ⚔ **Auto-install** – Automatically install dependencies after setup
22
+
23
+ ### Quick Install with Defaults
24
+
25
+ Provide the project name as an argument to skip the first prompt:
8
26
 
9
27
  ```bash
10
28
  npm create tauri-app-project-wind@latest my-app
11
- cd my-app
12
- npm install
13
- npm run tauri dev
14
29
  ```
15
30
 
16
- ### yarn
31
+ ### Package Manager Specific
17
32
 
33
+ #### npm
18
34
  ```bash
19
- yarn create tauri-app-project-wind my-app
20
- cd my-app
21
- yarn install
22
- yarn tauri dev
35
+ npm create tauri-app-project-wind@latest my-app
23
36
  ```
24
37
 
25
- ### pnpm
38
+ #### yarn
39
+ ```bash
40
+ yarn create tauri-app-project-wind my-app
41
+ ```
26
42
 
43
+ #### pnpm
27
44
  ```bash
28
45
  pnpm create tauri-app-project-wind my-app
29
- cd my-app
30
- pnpm install
31
- pnpm tauri dev
46
+ ```
47
+
48
+ #### bun
49
+ ```bash
50
+ bun create tauri-app-project-wind my-app
32
51
  ```
33
52
 
34
53
  ## ✨ What's Included
package/index.js CHANGED
@@ -3,33 +3,281 @@
3
3
  const { execSync } = require('child_process');
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
+ const readline = require('readline');
6
7
 
7
- const projectName = process.argv[2] || 'project-wind-app';
8
+ // ANSI color codes for better CLI experience
9
+ const colors = {
10
+ reset: '\x1b[0m',
11
+ bright: '\x1b[1m',
12
+ dim: '\x1b[2m',
13
+ cyan: '\x1b[36m',
14
+ green: '\x1b[32m',
15
+ yellow: '\x1b[33m',
16
+ blue: '\x1b[34m',
17
+ magenta: '\x1b[35m',
18
+ };
8
19
 
9
- console.log(`\nšŸŒ¬ļø Creating Project Wind app in ${projectName}...\n`);
20
+ // Readline interface for user input
21
+ const rl = readline.createInterface({
22
+ input: process.stdin,
23
+ output: process.stdout
24
+ });
10
25
 
11
- // Clone the repository
12
- console.log('šŸ“¦ Cloning template...');
13
- execSync(
14
- `git clone --depth 1 https://github.com/ssanaullahrais/project-wind-tauri-js-starter-kit.git ${projectName}`,
15
- { stdio: 'inherit' }
16
- );
26
+ // Utility to ask questions
27
+ function question(query) {
28
+ return new Promise(resolve => rl.question(query, resolve));
29
+ }
30
+
31
+ // Validate project name
32
+ function validateProjectName(name) {
33
+ if (!name) return false;
34
+ if (!/^[a-z0-9-_]+$/i.test(name)) {
35
+ console.log(`${colors.yellow}āš ļø Project name can only contain letters, numbers, hyphens, and underscores${colors.reset}`);
36
+ return false;
37
+ }
38
+ return true;
39
+ }
17
40
 
18
- // Remove .git directory
19
- const gitDir = path.join(process.cwd(), projectName, '.git');
20
- if (fs.existsSync(gitDir)) {
21
- fs.rmSync(gitDir, { recursive: true, force: true });
41
+ // Validate bundle identifier
42
+ function validateBundleId(id) {
43
+ if (!id) return false;
44
+ if (!/^[a-z][a-z0-9-]*(\.[a-z][a-z0-9-]*)+$/i.test(id)) {
45
+ console.log(`${colors.yellow}āš ļø Bundle ID should be in reverse domain format (e.g., com.company.app)${colors.reset}`);
46
+ return false;
47
+ }
48
+ return true;
22
49
  }
23
50
 
24
- // Remove cli directory from cloned project
25
- const cliDir = path.join(process.cwd(), projectName, 'cli');
26
- if (fs.existsSync(cliDir)) {
27
- fs.rmSync(cliDir, { recursive: true, force: true });
51
+ // Main function
52
+ async function main() {
53
+ console.log(`${colors.bright}${colors.cyan}`);
54
+ console.log('╔══════════════════════════════════════════════════════════╗');
55
+ console.log('ā•‘ ā•‘');
56
+ console.log('ā•‘ šŸŒ¬ļø Project Wind CLI Installer šŸŒ¬ļø ā•‘');
57
+ console.log('ā•‘ ā•‘');
58
+ console.log('ā•‘ Modern Tauri + React + TypeScript + Shadcn/UI ā•‘');
59
+ console.log('ā•‘ ā•‘');
60
+ console.log('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•');
61
+ console.log(colors.reset);
62
+ console.log(`${colors.dim}Let's set up your new desktop application!${colors.reset}\n`);
63
+
64
+ let config = {};
65
+
66
+ // 1. Project folder name
67
+ console.log(`${colors.cyan}šŸ“ Project folder name:${colors.reset}`);
68
+ console.log(` ${colors.dim}This will be your project directory name${colors.reset}`);
69
+ console.log(` ${colors.dim}Use lowercase with dashes (e.g., my-awesome-app)${colors.reset}`);
70
+ let projectName = process.argv[2];
71
+ while (!validateProjectName(projectName)) {
72
+ projectName = await question(` > `);
73
+ }
74
+ config.projectName = projectName.trim();
75
+
76
+ // 2. App display name
77
+ const autoDisplayName = config.projectName.split('-').map(word =>
78
+ word.charAt(0).toUpperCase() + word.slice(1)
79
+ ).join(' ');
80
+ console.log(`\n${colors.cyan}šŸ·ļø App display name:${colors.reset}`);
81
+ console.log(` ${colors.dim}This is the name shown in your app's titlebar and windows${colors.reset}`);
82
+ console.log(` ${colors.dim}Can use spaces and capitals (e.g., My Awesome App)${colors.reset}`);
83
+ console.log(` ${colors.dim}Press Enter to use: "${autoDisplayName}"${colors.reset}`);
84
+ let appName = await question(` > `);
85
+ config.appName = appName.trim() || autoDisplayName;
86
+
87
+ // 3. Author name
88
+ let author = await question(`\n${colors.cyan}šŸ‘¤ Author name:${colors.reset} ${colors.dim}(your name or company)${colors.reset}\n > `);
89
+ config.author = author.trim() || 'Your Name';
90
+
91
+ // 4. Bundle identifier
92
+ let bundleId = '';
93
+ const defaultBundleId = `com.${config.author.toLowerCase().replace(/\s+/g, '')}.${config.projectName.replace(/[^a-z0-9]/gi, '')}`;
94
+ while (!validateBundleId(bundleId)) {
95
+ bundleId = await question(`\n${colors.cyan}šŸ“¦ Bundle identifier:${colors.reset} ${colors.dim}(defaults to "${defaultBundleId}")${colors.reset}\n > `);
96
+ if (!bundleId.trim()) {
97
+ bundleId = defaultBundleId;
98
+ }
99
+ }
100
+ config.bundleId = bundleId.trim();
101
+
102
+ // 5. Package manager
103
+ console.log(`\n${colors.cyan}šŸ“¦ Package manager:${colors.reset}`);
104
+ console.log(` ${colors.dim}1) npm${colors.reset}`);
105
+ console.log(` ${colors.dim}2) yarn${colors.reset}`);
106
+ console.log(` ${colors.dim}3) pnpm${colors.reset}`);
107
+ console.log(` ${colors.dim}4) bun${colors.reset}`);
108
+ let pmChoice = await question(` > `);
109
+ config.packageManager = ['npm', 'yarn', 'pnpm', 'bun'][parseInt(pmChoice) - 1] || 'npm';
110
+
111
+ // 6. Auto install dependencies
112
+ let autoInstall = await question(`\n${colors.cyan}⚔ Install dependencies automatically?${colors.reset} ${colors.dim}(y/n, default: y)${colors.reset}\n > `);
113
+ config.autoInstall = autoInstall.trim().toLowerCase() !== 'n';
114
+
115
+ // Set defaults for non-prompted options
116
+ config.description = `A modern desktop application built with Tauri and React`;
117
+ config.theme = 'system';
118
+ config.initGit = false;
119
+ config.includeDemo = true;
120
+
121
+ rl.close();
122
+
123
+ // Display configuration summary
124
+ console.log(`\n${colors.bright}${colors.magenta}šŸ“‹ Configuration Summary:${colors.reset}`);
125
+ console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}`);
126
+ console.log(` Project Name: ${colors.green}${config.projectName}${colors.reset}`);
127
+ console.log(` App Name: ${colors.green}${config.appName}${colors.reset}`);
128
+ console.log(` Author: ${colors.green}${config.author}${colors.reset}`);
129
+ console.log(` Bundle ID: ${colors.green}${config.bundleId}${colors.reset}`);
130
+ console.log(` Package Manager: ${colors.green}${config.packageManager}${colors.reset}`);
131
+ console.log(`${colors.dim}${'─'.repeat(60)}${colors.reset}\n`);
132
+
133
+ // Clone the repository
134
+ console.log(`${colors.blue}šŸ“¦ Cloning template...${colors.reset}`);
135
+ try {
136
+ execSync(
137
+ `git clone --depth 1 https://github.com/ssanaullahrais/project-wind-tauri-js-starter-kit.git ${config.projectName}`,
138
+ { stdio: 'inherit' }
139
+ );
140
+ } catch (error) {
141
+ console.error(`${colors.yellow}āš ļø Failed to clone repository. Make sure git is installed.${colors.reset}`);
142
+ process.exit(1);
143
+ }
144
+
145
+ const projectPath = path.join(process.cwd(), config.projectName);
146
+
147
+ // Remove .git directory
148
+ console.log(`${colors.blue}🧹 Cleaning up...${colors.reset}`);
149
+ const gitDir = path.join(projectPath, '.git');
150
+ if (fs.existsSync(gitDir)) {
151
+ fs.rmSync(gitDir, { recursive: true, force: true });
152
+ }
153
+
154
+ // Remove cli directory from cloned project
155
+ const cliDir = path.join(projectPath, 'cli');
156
+ if (fs.existsSync(cliDir)) {
157
+ fs.rmSync(cliDir, { recursive: true, force: true });
158
+ }
159
+
160
+ // Remove demo components if not wanted
161
+ if (!config.includeDemo) {
162
+ console.log(`${colors.blue}šŸ—‘ļø Removing demo components...${colors.reset}`);
163
+ const demoComponentPath = path.join(projectPath, 'src', 'components', 'demo');
164
+ if (fs.existsSync(demoComponentPath)) {
165
+ fs.rmSync(demoComponentPath, { recursive: true, force: true });
166
+ }
167
+ }
168
+
169
+ // Update configuration files
170
+ console.log(`${colors.blue}āš™ļø Configuring project...${colors.reset}`);
171
+
172
+ // Update package.json
173
+ const packageJsonPath = path.join(projectPath, 'package.json');
174
+ if (fs.existsSync(packageJsonPath)) {
175
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
176
+ packageJson.name = config.projectName;
177
+ packageJson.description = config.description;
178
+ packageJson.author = config.author;
179
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
180
+ }
181
+
182
+ // Update tauri.conf.json
183
+ const tauriConfPath = path.join(projectPath, 'src-tauri', 'tauri.conf.json');
184
+ if (fs.existsSync(tauriConfPath)) {
185
+ const tauriConf = JSON.parse(fs.readFileSync(tauriConfPath, 'utf8'));
186
+ tauriConf.package.productName = config.appName;
187
+ tauriConf.tauri.windows[0].title = config.appName;
188
+ tauriConf.tauri.bundle.identifier = config.bundleId;
189
+ fs.writeFileSync(tauriConfPath, JSON.stringify(tauriConf, null, 2));
190
+ }
191
+
192
+ // Update index.html
193
+ const indexHtmlPath = path.join(projectPath, 'index.html');
194
+ if (fs.existsSync(indexHtmlPath)) {
195
+ let indexHtml = fs.readFileSync(indexHtmlPath, 'utf8');
196
+ indexHtml = indexHtml.replace(/<title>.*?<\/title>/, `<title>${config.appName}</title>`);
197
+ fs.writeFileSync(indexHtmlPath, indexHtml);
198
+ }
199
+
200
+ // Update TitleBar.tsx
201
+ const titleBarPath = path.join(projectPath, 'src', 'components', 'TitleBar.tsx');
202
+ if (fs.existsSync(titleBarPath)) {
203
+ let titleBar = fs.readFileSync(titleBarPath, 'utf8');
204
+ // Replace all instances of "Project Wind" with the app name
205
+ titleBar = titleBar.replace(/Project Wind/g, config.appName);
206
+ // Update copyright year and author
207
+ const currentYear = new Date().getFullYear();
208
+ titleBar = titleBar.replace(/Ā© \d{4} .*?\. All rights reserved\./, `Ā© ${currentYear} ${config.author}. All rights reserved.`);
209
+ // Update created by
210
+ titleBar = titleBar.replace(/<span className="font-medium">Sunny<\/span>/, `<span className="font-medium">${config.author}</span>`);
211
+ fs.writeFileSync(titleBarPath, titleBar);
212
+ }
213
+
214
+ // Update theme-provider.tsx if theme is not system
215
+ if (config.theme !== 'system') {
216
+ const themeProviderPath = path.join(projectPath, 'src', 'components', 'theme-provider.tsx');
217
+ if (fs.existsSync(themeProviderPath)) {
218
+ let themeProvider = fs.readFileSync(themeProviderPath, 'utf8');
219
+ themeProvider = themeProvider.replace(/defaultTheme="system"/, `defaultTheme="${config.theme}"`);
220
+ fs.writeFileSync(themeProviderPath, themeProvider);
221
+ }
222
+ }
223
+
224
+ // Update DemoComponents.tsx if it exists
225
+ if (config.includeDemo) {
226
+ const demoComponentsPath = path.join(projectPath, 'src', 'components', 'demo', 'DemoComponents.tsx');
227
+ if (fs.existsSync(demoComponentsPath)) {
228
+ let demoComponents = fs.readFileSync(demoComponentsPath, 'utf8');
229
+ demoComponents = demoComponents.replace(/Project Wind/g, config.appName);
230
+ fs.writeFileSync(demoComponentsPath, demoComponents);
231
+ }
232
+ }
233
+
234
+ // Initialize git if requested
235
+ if (config.initGit) {
236
+ console.log(`${colors.blue}šŸ”§ Initializing git repository...${colors.reset}`);
237
+ try {
238
+ execSync('git init', { cwd: projectPath, stdio: 'ignore' });
239
+ execSync('git add .', { cwd: projectPath, stdio: 'ignore' });
240
+ execSync(`git commit -m "Initial commit: ${config.appName}"`, { cwd: projectPath, stdio: 'ignore' });
241
+ } catch (error) {
242
+ console.log(`${colors.yellow}āš ļø Git initialization failed (git may not be installed)${colors.reset}`);
243
+ }
244
+ }
245
+
246
+ // Install dependencies if requested
247
+ if (config.autoInstall) {
248
+ console.log(`\n${colors.blue}šŸ“„ Installing dependencies with ${config.packageManager}...${colors.reset}`);
249
+ console.log(`${colors.dim}This may take a few minutes...${colors.reset}\n`);
250
+ try {
251
+ const installCmd = config.packageManager === 'npm' ? 'npm install' :
252
+ config.packageManager === 'yarn' ? 'yarn' :
253
+ config.packageManager === 'pnpm' ? 'pnpm install' :
254
+ 'bun install';
255
+ execSync(installCmd, { cwd: projectPath, stdio: 'inherit' });
256
+ } catch (error) {
257
+ console.log(`${colors.yellow}āš ļø Dependency installation failed. You can install them manually later.${colors.reset}`);
258
+ }
259
+ }
260
+
261
+ // Success message
262
+ console.log(`\n${colors.bright}${colors.green}āœ… Project created successfully!${colors.reset}\n`);
263
+ console.log(`${colors.bright}šŸ“ Next steps:${colors.reset}\n`);
264
+ console.log(` ${colors.cyan}cd ${config.projectName}${colors.reset}`);
265
+
266
+ if (!config.autoInstall) {
267
+ console.log(` ${colors.cyan}${config.packageManager} install${colors.reset}`);
268
+ }
269
+
270
+ const devCmd = config.packageManager === 'npm' ? 'npm run tauri dev' :
271
+ config.packageManager === 'yarn' ? 'yarn tauri dev' :
272
+ config.packageManager === 'pnpm' ? 'pnpm tauri dev' :
273
+ 'bun run tauri dev';
274
+ console.log(` ${colors.cyan}${devCmd}${colors.reset}\n`);
275
+
276
+ console.log(`${colors.bright}${colors.magenta}šŸš€ Happy coding!${colors.reset}\n`);
28
277
  }
29
278
 
30
- console.log('\nāœ… Project created successfully!\n');
31
- console.log('šŸ“ Next steps:\n');
32
- console.log(` cd ${projectName}`);
33
- console.log(' npm install');
34
- console.log(' npm run tauri dev\n');
35
- console.log('šŸš€ Happy coding!\n');
279
+ // Run the main function
280
+ main().catch(error => {
281
+ console.error(`${colors.yellow}āŒ An error occurred:${colors.reset}`, error.message);
282
+ process.exit(1);
283
+ });
package/package.json CHANGED
@@ -1,11 +1,15 @@
1
1
  {
2
2
  "name": "create-tauri-app-project-wind",
3
- "version": "1.0.2",
4
- "description": "Create a new Project Wind Tauri app",
3
+ "version": "1.1.0",
4
+ "description": "Interactive CLI to create a customized Tauri + React + TypeScript desktop application",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "create-tauri-app-project-wind": "./index.js"
8
8
  },
9
+ "files": [
10
+ "index.js",
11
+ "README.md"
12
+ ],
9
13
  "keywords": [
10
14
  "tauri",
11
15
  "react",
@@ -14,13 +18,18 @@
14
18
  "desktop",
15
19
  "starter",
16
20
  "template",
17
- "project-wind"
21
+ "project-wind",
22
+ "cli",
23
+ "create",
24
+ "scaffolding",
25
+ "boilerplate"
18
26
  ],
19
27
  "author": "Sunny",
20
28
  "license": "MIT",
21
29
  "repository": {
22
30
  "type": "git",
23
- "url": "https://github.com/ssanaullahrais/project-wind-tauri-js-starter-kit.git"
31
+ "url": "https://github.com/ssanaullahrais/project-wind-tauri-js-starter-kit.git",
32
+ "directory": "cli"
24
33
  },
25
34
  "bugs": {
26
35
  "url": "https://github.com/ssanaullahrais/project-wind-tauri-js-starter-kit/issues"
@@ -28,5 +37,9 @@
28
37
  "homepage": "https://github.com/ssanaullahrais/project-wind-tauri-js-starter-kit#readme",
29
38
  "engines": {
30
39
  "node": ">=18.0.0"
31
- }
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "preferGlobal": true
32
45
  }