@iselect/select 1.0.0 → 1.0.2

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.
package/bin/select.js CHANGED
@@ -33,7 +33,7 @@ program
33
33
  .option('-t, --template <template>', 'Project template (react, vue, vanilla)', 'react')
34
34
  .action(init);
35
35
 
36
- // select - Interactive platform selection
36
+ // slct select - Interactive platform selection
37
37
  program
38
38
  .command('select')
39
39
  .alias('s')
@@ -47,6 +47,8 @@ program
47
47
  .option('--tauri', 'Use Tauri for desktop (default)')
48
48
  .option('--electron', 'Use Electron for desktop')
49
49
  .option('--release', 'Build for production release')
50
+ .option('--static', 'Copy files directly without bundling (vanilla HTML/CSS/JS)')
51
+ .option('--inline', 'Inline CSS and JS into a single HTML file')
50
52
  .action(build);
51
53
 
52
54
  // slct deploy - Deploy to Select marketplace
@@ -65,3 +67,4 @@ program
65
67
 
66
68
  // Parse arguments
67
69
  program.parse();
70
+
@@ -55,25 +55,348 @@ async function build(target, options) {
55
55
  }
56
56
  }
57
57
 
58
+ // ==================== FRAMEWORK SIGNATURES (Vercel-style) ====================
59
+ const FRAMEWORK_SIGNATURES = {
60
+ // Next.js
61
+ 'next.config.js': { name: 'Next.js', build: 'npm run build', output: '.next', icon: '▲' },
62
+ 'next.config.mjs': { name: 'Next.js', build: 'npm run build', output: '.next', icon: '▲' },
63
+ 'next.config.ts': { name: 'Next.js', build: 'npm run build', output: '.next', icon: '▲' },
64
+
65
+ // Astro
66
+ 'astro.config.mjs': { name: 'Astro', build: 'npm run build', output: 'dist', icon: '🚀' },
67
+ 'astro.config.js': { name: 'Astro', build: 'npm run build', output: 'dist', icon: '🚀' },
68
+ 'astro.config.ts': { name: 'Astro', build: 'npm run build', output: 'dist', icon: '🚀' },
69
+
70
+ // SvelteKit
71
+ 'svelte.config.js': { name: 'SvelteKit', build: 'npm run build', output: 'build', icon: '🔶' },
72
+ 'svelte.config.ts': { name: 'SvelteKit', build: 'npm run build', output: 'build', icon: '🔶' },
73
+
74
+ // Nuxt
75
+ 'nuxt.config.js': { name: 'Nuxt', build: 'npm run build', output: '.nuxt', icon: '💚' },
76
+ 'nuxt.config.ts': { name: 'Nuxt', build: 'npm run build', output: '.nuxt', icon: '💚' },
77
+
78
+ // Remix
79
+ 'remix.config.js': { name: 'Remix', build: 'npm run build', output: 'build', icon: '💿' },
80
+
81
+ // Gatsby
82
+ 'gatsby-config.js': { name: 'Gatsby', build: 'npm run build', output: 'public', icon: '💜' },
83
+ 'gatsby-config.ts': { name: 'Gatsby', build: 'npm run build', output: 'public', icon: '💜' },
84
+
85
+ // Vite (generic)
86
+ 'vite.config.js': { name: 'Vite', build: 'npm run build', output: 'dist', icon: '⚡' },
87
+ 'vite.config.ts': { name: 'Vite', build: 'npm run build', output: 'dist', icon: '⚡' },
88
+ 'vite.config.mjs': { name: 'Vite', build: 'npm run build', output: 'dist', icon: '⚡' },
89
+
90
+ // Angular
91
+ 'angular.json': { name: 'Angular', build: 'npm run build', output: 'dist', icon: '🅰️' },
92
+
93
+ // Vue CLI
94
+ 'vue.config.js': { name: 'Vue CLI', build: 'npm run build', output: 'dist', icon: '💚' },
95
+ };
96
+
97
+ // Package.json dependency-based detection
98
+ const DEPENDENCY_SIGNATURES = {
99
+ 'next': { name: 'Next.js', build: 'npm run build', output: '.next', icon: '▲' },
100
+ '@remix-run/react': { name: 'Remix', build: 'npm run build', output: 'build', icon: '💿' },
101
+ 'gatsby': { name: 'Gatsby', build: 'npm run build', output: 'public', icon: '💜' },
102
+ '@angular/core': { name: 'Angular', build: 'npm run build', output: 'dist', icon: '🅰️' },
103
+ 'react-scripts': { name: 'Create React App', build: 'npm run build', output: 'build', icon: '⚛️' },
104
+ };
105
+
106
+ // ==================== FRAMEWORK DETECTION ====================
107
+ async function detectFramework(projectPath) {
108
+ // 1. Check for framework config files
109
+ for (const [configFile, framework] of Object.entries(FRAMEWORK_SIGNATURES)) {
110
+ if (await fs.pathExists(path.join(projectPath, configFile))) {
111
+ return { ...framework, detected: 'config', configFile };
112
+ }
113
+ }
114
+
115
+ // 2. Check package.json dependencies
116
+ const pkgPath = path.join(projectPath, 'package.json');
117
+ if (await fs.pathExists(pkgPath)) {
118
+ try {
119
+ const pkg = await fs.readJson(pkgPath);
120
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
121
+
122
+ for (const [dep, framework] of Object.entries(DEPENDENCY_SIGNATURES)) {
123
+ if (allDeps[dep]) {
124
+ return { ...framework, detected: 'dependency', dependency: dep };
125
+ }
126
+ }
127
+
128
+ // Check for generic build script
129
+ if (pkg.scripts?.build) {
130
+ return {
131
+ name: 'Custom',
132
+ build: 'npm run build',
133
+ output: 'dist',
134
+ icon: '📦',
135
+ detected: 'build-script'
136
+ };
137
+ }
138
+ } catch { }
139
+ }
140
+
141
+ // 3. Static site detection
142
+ const hasIndexHtml = await fs.pathExists(path.join(projectPath, 'index.html'));
143
+ if (hasIndexHtml) {
144
+ return {
145
+ name: 'Static',
146
+ build: null, // No build needed
147
+ output: '.',
148
+ icon: '📄',
149
+ detected: 'static'
150
+ };
151
+ }
152
+
153
+ return null;
154
+ }
155
+
156
+ // ==================== PROJECT TYPE DETECTION (legacy wrapper) ====================
157
+ async function detectProjectType(projectPath) {
158
+ const framework = await detectFramework(projectPath);
159
+
160
+ if (!framework) return 'unknown';
161
+ if (framework.detected === 'static') return 'static';
162
+ if (framework.name === 'Vite') return 'vite';
163
+ return 'bundled';
164
+ }
165
+
166
+ // ==================== INLINE BUILD ====================
167
+ async function buildInline(projectPath, outputDir) {
168
+ const spinner = ora('Building inline HTML...').start();
169
+
170
+ // Ensure output dir exists
171
+ await fs.ensureDir(outputDir);
172
+
173
+ // Read index.html
174
+ const indexPath = path.join(projectPath, 'index.html');
175
+ if (!await fs.pathExists(indexPath)) {
176
+ spinner.fail('No index.html found');
177
+ throw new Error('index.html not found in project root');
178
+ }
179
+
180
+ let html = await fs.readFile(indexPath, 'utf8');
181
+
182
+ // Inline CSS files
183
+ const cssLinkRegex = /<link[^>]+rel=["']stylesheet["'][^>]+href=["']([^"']+)["'][^>]*>/gi;
184
+ const cssLinks = [];
185
+ let match;
186
+ while ((match = cssLinkRegex.exec(html)) !== null) {
187
+ cssLinks.push({ full: match[0], href: match[1] });
188
+ }
189
+
190
+ for (const link of cssLinks) {
191
+ const cssPath = path.join(projectPath, link.href);
192
+ if (await fs.pathExists(cssPath)) {
193
+ const cssContent = await fs.readFile(cssPath, 'utf8');
194
+ html = html.replace(link.full, `<style>\n${cssContent}\n</style>`);
195
+ }
196
+ }
197
+
198
+ // Inline JS files
199
+ const jsScriptRegex = /<script[^>]+src=["']([^"']+)["'][^>]*><\/script>/gi;
200
+ const jsScripts = [];
201
+ while ((match = jsScriptRegex.exec(html)) !== null) {
202
+ jsScripts.push({ full: match[0], src: match[1] });
203
+ }
204
+
205
+ for (const script of jsScripts) {
206
+ const jsPath = path.join(projectPath, script.src);
207
+ if (await fs.pathExists(jsPath)) {
208
+ const jsContent = await fs.readFile(jsPath, 'utf8');
209
+ html = html.replace(script.full, `<script>\n${jsContent}\n</script>`);
210
+ }
211
+ }
212
+
213
+ // Write output
214
+ await fs.writeFile(path.join(outputDir, 'index.html'), html);
215
+
216
+ spinner.succeed(`Created single-file HTML (${cssLinks.length} CSS, ${jsScripts.length} JS inlined)`);
217
+
218
+ return { cssInlined: cssLinks.length, jsInlined: jsScripts.length };
219
+ }
220
+
221
+ // ==================== STATIC FILE COPY ====================
222
+ async function copyStaticFiles(projectPath, outputDir) {
223
+ const spinner = ora('Copying static files...').start();
224
+
225
+ // Ensure output dir exists and is clean
226
+ await fs.emptyDir(outputDir);
227
+
228
+ // Files and folders to copy
229
+ const filesToCopy = ['index.html', 'style.css', 'styles.css', 'main.css', 'app.css'];
230
+ const foldersToCheck = ['assets', 'css', 'js', 'images', 'fonts', 'src'];
231
+
232
+ let copiedFiles = 0;
233
+ let cssFound = false;
234
+
235
+ // Copy individual files
236
+ for (const file of filesToCopy) {
237
+ const srcPath = path.join(projectPath, file);
238
+ if (await fs.pathExists(srcPath)) {
239
+ await fs.copy(srcPath, path.join(outputDir, file));
240
+ copiedFiles++;
241
+ if (file.endsWith('.css')) cssFound = true;
242
+ }
243
+ }
244
+
245
+ // Copy .js files from root
246
+ const rootFiles = await fs.readdir(projectPath);
247
+ for (const file of rootFiles) {
248
+ if (file.endsWith('.js') && !file.includes('.config.')) {
249
+ await fs.copy(path.join(projectPath, file), path.join(outputDir, file));
250
+ copiedFiles++;
251
+ }
252
+ if (file.endsWith('.css')) {
253
+ await fs.copy(path.join(projectPath, file), path.join(outputDir, file));
254
+ copiedFiles++;
255
+ cssFound = true;
256
+ }
257
+ }
258
+
259
+ // Copy folders
260
+ for (const folder of foldersToCheck) {
261
+ const srcPath = path.join(projectPath, folder);
262
+ if (await fs.pathExists(srcPath)) {
263
+ await fs.copy(srcPath, path.join(outputDir, folder));
264
+ copiedFiles++;
265
+
266
+ // Check for CSS in folders
267
+ const folderFiles = await fs.readdir(srcPath);
268
+ if (folderFiles.some(f => f.endsWith('.css'))) {
269
+ cssFound = true;
270
+ }
271
+ }
272
+ }
273
+
274
+ spinner.succeed(`Copied ${copiedFiles} files/folders to ${outputDir}`);
275
+
276
+ // Warning if no CSS found
277
+ if (!cssFound) {
278
+ console.log(chalk.yellow('\n⚠️ Warning: No CSS files found in build output'));
279
+ console.log(chalk.gray(' Ensure your HTML links to the CSS file correctly\n'));
280
+ }
281
+
282
+ return { copiedFiles, cssFound };
283
+ }
284
+
58
285
  // ==================== WEB BUILD ====================
59
286
  async function buildWeb(config, options) {
60
287
  console.log(chalk.blue('\n📦 Building for Web...\n'));
61
288
 
62
- const spinner = ora('Running build command...').start();
289
+ const projectPath = process.cwd();
290
+ const outputDir = path.resolve(projectPath, config.build?.outDir || 'dist');
291
+
292
+ // Check for --static flag
293
+ if (options.static) {
294
+ console.log(chalk.gray(' Mode: Static (--static flag)'));
295
+ await copyStaticFiles(projectPath, outputDir);
296
+ console.log(chalk.green('\n✅ Static web build complete!'));
297
+ console.log(chalk.gray(` Output: ${outputDir}/\n`));
298
+ return;
299
+ }
300
+
301
+ // Check for --inline flag
302
+ if (options.inline) {
303
+ console.log(chalk.gray(' Mode: Inline (--inline flag)'));
304
+ await buildInline(projectPath, outputDir);
305
+ console.log(chalk.green('\n✅ Inline web build complete!'));
306
+ console.log(chalk.gray(` Output: ${outputDir}/index.html (single file)\n`));
307
+ return;
308
+ }
309
+
310
+ // Auto-detect framework (Vercel-style)
311
+ const framework = await detectFramework(projectPath);
312
+
313
+ if (!framework) {
314
+ console.log(chalk.yellow('\n⚠️ Could not detect project type'));
315
+ console.log(chalk.gray(' No index.html or package.json found'));
316
+ console.log(chalk.gray(' Try: slct build web --static\n'));
317
+ process.exit(1);
318
+ }
319
+
320
+ // Display detected framework
321
+ console.log(chalk.cyan(` ${framework.icon} Detected: ${chalk.bold(framework.name)}`));
322
+ if (framework.detected === 'config') {
323
+ console.log(chalk.gray(` via ${framework.configFile}`));
324
+ } else if (framework.detected === 'dependency') {
325
+ console.log(chalk.gray(` via ${framework.dependency} dependency`));
326
+ } else if (framework.detected === 'static') {
327
+ console.log(chalk.gray(' (static HTML/CSS/JS site)'));
328
+ }
329
+ console.log('');
330
+
331
+ // Handle static sites
332
+ if (framework.detected === 'static') {
333
+ await copyStaticFiles(projectPath, outputDir);
334
+ console.log(chalk.green('\n✅ Static web build complete!'));
335
+ console.log(chalk.gray(` Output: ${outputDir}/\n`));
336
+ return;
337
+ }
338
+
339
+ // Run build command for bundled projects
340
+ // Priority: select.json config > detected framework > default
341
+ const buildCommand = config.build?.command || framework.build || 'npm run build';
342
+ const finalOutputDir = config.build?.outDir
343
+ ? path.resolve(projectPath, config.build.outDir)
344
+ : path.resolve(projectPath, framework.output || 'dist');
345
+
346
+ const spinner = ora(`Running: ${chalk.cyan(buildCommand)}`).start();
63
347
 
64
348
  try {
65
- execSync(config.build?.command || 'npm run build', {
349
+ execSync(buildCommand, {
66
350
  stdio: 'inherit',
67
- cwd: process.cwd()
351
+ cwd: projectPath
68
352
  });
69
- spinner.succeed('Web build complete!');
70
- console.log(chalk.gray(` Output: ${config.build?.outDir || 'dist'}/\n`));
353
+ spinner.succeed(`${framework.icon} ${framework.name} build complete!`);
354
+
355
+ // Validate output
356
+ await validateBuildOutput(finalOutputDir);
357
+
358
+ console.log(chalk.gray(` Output: ${finalOutputDir}/\n`));
71
359
  } catch (error) {
72
360
  spinner.fail('Web build failed');
73
361
  throw error;
74
362
  }
75
363
  }
76
364
 
365
+ // ==================== BUILD VALIDATION ====================
366
+ async function validateBuildOutput(outputDir) {
367
+ if (!await fs.pathExists(outputDir)) {
368
+ console.log(chalk.yellow('\n⚠️ Warning: Output directory not found'));
369
+ return;
370
+ }
371
+
372
+ const files = await fs.readdir(outputDir);
373
+
374
+ // Check for index.html
375
+ if (!files.includes('index.html')) {
376
+ console.log(chalk.yellow('\n⚠️ Warning: No index.html found in build output'));
377
+ }
378
+
379
+ // Check for CSS
380
+ const hasCss = files.some(f => f.endsWith('.css'));
381
+ const hasAssetsFolder = files.includes('assets');
382
+
383
+ if (!hasCss && hasAssetsFolder) {
384
+ const assetsPath = path.join(outputDir, 'assets');
385
+ if (await fs.pathExists(assetsPath)) {
386
+ const assetFiles = await fs.readdir(assetsPath);
387
+ if (assetFiles.some(f => f.endsWith('.css'))) {
388
+ return; // CSS is in assets folder, all good
389
+ }
390
+ }
391
+ }
392
+
393
+ if (!hasCss) {
394
+ console.log(chalk.yellow('\n⚠️ Warning: No CSS files found in build output'));
395
+ console.log(chalk.gray(' If using vanilla CSS, ensure it\'s imported in your JS entry point'));
396
+ console.log(chalk.gray(' Or use: slct build web --static\n'));
397
+ }
398
+ }
399
+
77
400
  // ==================== DESKTOP BUILD ====================
78
401
  async function buildDesktop(config, options) {
79
402
  const framework = options.electron ? 'electron' : (config.desktop?.framework || 'tauri');
@@ -39,8 +39,8 @@ async function deploy(options) {
39
39
  type: 'list',
40
40
  name: 'category',
41
41
  message: 'Category:',
42
- choices: ['Productivity', 'Finance', 'Developer Tools', 'Education', 'Entertainment', 'Utilities', 'Other'],
43
- default: config.category || 'Productivity'
42
+ choices: ['productivity', 'finance', 'developer-tools', 'education', 'entertainment', 'utilities', 'games', 'ai-tools', 'experiments'],
43
+ default: config.category || 'productivity'
44
44
  },
45
45
  {
46
46
  type: 'password',
@@ -66,7 +66,7 @@ async function deploy(options) {
66
66
  ...config,
67
67
  name: answers.name,
68
68
  tagline: answers.tagline,
69
- category: answers.category.toLowerCase().replace(' ', '-'),
69
+ category: answers.category,
70
70
  platforms: []
71
71
  };
72
72
 
@@ -5,125 +5,125 @@ const inquirer = require('inquirer');
5
5
  const ora = require('ora');
6
6
 
7
7
  async function init(name, options) {
8
- console.log(chalk.bold('\n📦 Initialize Select Project\n'));
9
-
10
- // If no name provided, ask for it
11
- if (!name) {
12
- const answers = await inquirer.prompt([
13
- {
14
- type: 'input',
15
- name: 'name',
16
- message: 'Project name:',
17
- default: 'my-select-app',
18
- validate: (input) => {
19
- if (/^[a-z0-9-]+$/.test(input)) return true;
20
- return 'Project name must be lowercase with hyphens only';
21
- }
22
- }
23
- ]);
24
- name = answers.name;
25
- }
26
-
27
- const projectDir = path.resolve(process.cwd(), name);
8
+ console.log(chalk.bold('\n📦 Initialize Select Project\n'));
28
9
 
29
- // Check if directory exists
30
- if (await fs.pathExists(projectDir)) {
31
- console.log(chalk.red(`\n❌ Directory "${name}" already exists.\n`));
32
- process.exit(1);
33
- }
34
-
35
- // Ask for project details
10
+ // If no name provided, ask for it
11
+ if (!name) {
36
12
  const answers = await inquirer.prompt([
37
- {
38
- type: 'list',
39
- name: 'template',
40
- message: 'Select a template:',
41
- choices: [
42
- { name: 'React + Vite', value: 'react' },
43
- { name: 'Vue + Vite', value: 'vue' },
44
- { name: 'Vanilla JavaScript', value: 'vanilla' }
45
- ],
46
- default: options.template
47
- },
48
- {
49
- type: 'checkbox',
50
- name: 'platforms',
51
- message: 'Target platforms:',
52
- choices: [
53
- { name: 'Web', value: 'web', checked: true },
54
- { name: 'Windows', value: 'windows' },
55
- { name: 'macOS', value: 'macos' },
56
- { name: 'Linux', value: 'linux' }
57
- ]
58
- },
59
- {
60
- type: 'list',
61
- name: 'desktopFramework',
62
- message: 'Desktop framework:',
63
- choices: [
64
- { name: 'Tauri (Recommended - smaller, faster)', value: 'tauri' },
65
- { name: 'Electron (Larger, more compatible)', value: 'electron' }
66
- ],
67
- when: (ans) => ans.platforms.some(p => ['windows', 'macos', 'linux'].includes(p))
13
+ {
14
+ type: 'input',
15
+ name: 'name',
16
+ message: 'Project name:',
17
+ default: 'my-select-app',
18
+ validate: (input) => {
19
+ if (/^[a-z0-9-]+$/.test(input)) return true;
20
+ return 'Project name must be lowercase with hyphens only';
68
21
  }
22
+ }
69
23
  ]);
24
+ name = answers.name;
25
+ }
26
+
27
+ const projectDir = path.resolve(process.cwd(), name);
28
+
29
+ // Check if directory exists
30
+ if (await fs.pathExists(projectDir)) {
31
+ console.log(chalk.red(`\n❌ Directory "${name}" already exists.\n`));
32
+ process.exit(1);
33
+ }
34
+
35
+ // Ask for project details
36
+ const answers = await inquirer.prompt([
37
+ {
38
+ type: 'list',
39
+ name: 'template',
40
+ message: 'Select a template:',
41
+ choices: [
42
+ { name: 'React + Vite', value: 'react' },
43
+ { name: 'Vue + Vite', value: 'vue' },
44
+ { name: 'Vanilla JavaScript', value: 'vanilla' }
45
+ ],
46
+ default: options.template
47
+ },
48
+ {
49
+ type: 'checkbox',
50
+ name: 'platforms',
51
+ message: 'Target platforms:',
52
+ choices: [
53
+ { name: 'Web', value: 'web', checked: true },
54
+ { name: 'Windows', value: 'windows' },
55
+ { name: 'macOS', value: 'macos' },
56
+ { name: 'Linux', value: 'linux' }
57
+ ]
58
+ },
59
+ {
60
+ type: 'list',
61
+ name: 'desktopFramework',
62
+ message: 'Desktop framework:',
63
+ choices: [
64
+ { name: 'Tauri (Recommended - smaller, faster)', value: 'tauri' },
65
+ { name: 'Electron (Larger, more compatible)', value: 'electron' }
66
+ ],
67
+ when: (ans) => ans.platforms.some(p => ['windows', 'macos', 'linux'].includes(p))
68
+ }
69
+ ]);
70
+
71
+ const spinner = ora('Creating project...').start();
72
+
73
+ try {
74
+ // Create project directory
75
+ await fs.ensureDir(projectDir);
76
+
77
+ // Create select.json config
78
+ const selectConfig = {
79
+ name: name,
80
+ version: '1.0.0',
81
+ description: 'A Select app',
82
+ platforms: answers.platforms,
83
+ desktop: {
84
+ framework: answers.desktopFramework || 'tauri'
85
+ },
86
+ build: {
87
+ command: 'npm run build',
88
+ devCommand: 'npm run dev',
89
+ outDir: 'dist'
90
+ }
91
+ };
92
+
93
+ await fs.writeJson(path.join(projectDir, 'select.json'), selectConfig, { spaces: 2 });
94
+
95
+ // Create basic package.json
96
+ const packageJson = {
97
+ name: name,
98
+ version: '1.0.0',
99
+ private: true,
100
+ scripts: {
101
+ dev: 'vite',
102
+ build: 'vite build',
103
+ preview: 'vite preview',
104
+ 'slct:build': 'slct build all',
105
+ 'slct:deploy': 'slct deploy'
106
+ },
107
+ dependencies: {},
108
+ devDependencies: {
109
+ 'vite': '^5.0.0'
110
+ }
111
+ };
112
+
113
+ // Add template-specific dependencies
114
+ if (answers.template === 'react') {
115
+ packageJson.dependencies['react'] = '^18.2.0';
116
+ packageJson.dependencies['react-dom'] = '^18.2.0';
117
+ packageJson.devDependencies['@vitejs/plugin-react'] = '^4.2.0';
118
+ } else if (answers.template === 'vue') {
119
+ packageJson.dependencies['vue'] = '^3.4.0';
120
+ packageJson.devDependencies['@vitejs/plugin-vue'] = '^4.5.0';
121
+ }
70
122
 
71
- const spinner = ora('Creating project...').start();
72
-
73
- try {
74
- // Create project directory
75
- await fs.ensureDir(projectDir);
76
-
77
- // Create select.json config
78
- const selectConfig = {
79
- name: name,
80
- version: '1.0.0',
81
- description: 'A Select app',
82
- platforms: answers.platforms,
83
- desktop: {
84
- framework: answers.desktopFramework || 'tauri'
85
- },
86
- build: {
87
- command: 'npm run build',
88
- devCommand: 'npm run dev',
89
- outDir: 'dist'
90
- }
91
- };
92
-
93
- await fs.writeJson(path.join(projectDir, 'select.json'), selectConfig, { spaces: 2 });
94
-
95
- // Create basic package.json
96
- const packageJson = {
97
- name: name,
98
- version: '1.0.0',
99
- private: true,
100
- scripts: {
101
- dev: 'vite',
102
- build: 'vite build',
103
- preview: 'vite preview',
104
- 'slct:build': 'slct build all',
105
- 'slct:deploy': 'slct deploy'
106
- },
107
- dependencies: {},
108
- devDependencies: {
109
- 'vite': '^5.0.0'
110
- }
111
- };
112
-
113
- // Add template-specific dependencies
114
- if (answers.template === 'react') {
115
- packageJson.dependencies['react'] = '^18.2.0';
116
- packageJson.dependencies['react-dom'] = '^18.2.0';
117
- packageJson.devDependencies['@vitejs/plugin-react'] = '^4.2.0';
118
- } else if (answers.template === 'vue') {
119
- packageJson.dependencies['vue'] = '^3.4.0';
120
- packageJson.devDependencies['@vitejs/plugin-vue'] = '^4.5.0';
121
- }
122
-
123
- await fs.writeJson(path.join(projectDir, 'package.json'), packageJson, { spaces: 2 });
123
+ await fs.writeJson(path.join(projectDir, 'package.json'), packageJson, { spaces: 2 });
124
124
 
125
- // Create basic index.html
126
- const indexHtml = `<!DOCTYPE html>
125
+ // Create basic index.html
126
+ const indexHtml = `<!DOCTYPE html>
127
127
  <html lang="en">
128
128
  <head>
129
129
  <meta charset="UTF-8">
@@ -136,21 +136,22 @@ async function init(name, options) {
136
136
  </body>
137
137
  </html>`;
138
138
 
139
- await fs.writeFile(path.join(projectDir, 'index.html'), indexHtml);
139
+ await fs.writeFile(path.join(projectDir, 'index.html'), indexHtml);
140
140
 
141
- // Create src directory with basic file
142
- await fs.ensureDir(path.join(projectDir, 'src'));
141
+ // Create src directory with basic file
142
+ await fs.ensureDir(path.join(projectDir, 'src'));
143
143
 
144
- // Create GitHub Actions workflow
145
- await createGithubWorkflow(projectDir, answers.desktopFramework || 'tauri', name);
144
+ // Create GitHub Actions workflow
145
+ await createGithubWorkflow(projectDir, answers.desktopFramework || 'tauri', name);
146
146
 
147
- if (answers.template === 'react') {
148
- await fs.writeFile(path.join(projectDir, 'src', 'main.jsx'), `import React from 'react'
147
+ if (answers.template === 'react') {
148
+ await fs.writeFile(path.join(projectDir, 'src', 'main.jsx'), `import React from 'react'
149
149
  import ReactDOM from 'react-dom/client'
150
+ import './style.css'
150
151
 
151
152
  function App() {
152
153
  return (
153
- <div style={{ padding: '2rem', fontFamily: 'system-ui' }}>
154
+ <div className="app">
154
155
  <h1>Welcome to ${name}</h1>
155
156
  <p>Built with Select CLI</p>
156
157
  </div>
@@ -159,19 +160,88 @@ function App() {
159
160
 
160
161
  ReactDOM.createRoot(document.getElementById('app')).render(<App />)
161
162
  `);
162
- } else {
163
- await fs.writeFile(path.join(projectDir, 'src', 'main.js'), `document.getElementById('app').innerHTML = \`
164
- <div style="padding: 2rem; font-family: system-ui;">
163
+ // Create React style.css
164
+ await fs.writeFile(path.join(projectDir, 'src', 'style.css'), `* {
165
+ margin: 0;
166
+ padding: 0;
167
+ box-sizing: border-box;
168
+ }
169
+
170
+ body {
171
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
172
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
173
+ min-height: 100vh;
174
+ display: flex;
175
+ align-items: center;
176
+ justify-content: center;
177
+ }
178
+
179
+ .app {
180
+ background: white;
181
+ padding: 3rem 4rem;
182
+ border-radius: 1rem;
183
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
184
+ text-align: center;
185
+ }
186
+
187
+ h1 {
188
+ color: #1a202c;
189
+ margin-bottom: 0.5rem;
190
+ }
191
+
192
+ p {
193
+ color: #718096;
194
+ }
195
+ `);
196
+ } else {
197
+ // Vanilla JS template
198
+ await fs.writeFile(path.join(projectDir, 'src', 'main.js'), `import './style.css'
199
+
200
+ document.getElementById('app').innerHTML = \`
201
+ <div class="app">
165
202
  <h1>Welcome to ${name}</h1>
166
203
  <p>Built with Select CLI</p>
167
204
  </div>
168
205
  \`
169
206
  `);
170
- }
207
+ // Create style.css for vanilla
208
+ await fs.writeFile(path.join(projectDir, 'src', 'style.css'), `* {
209
+ margin: 0;
210
+ padding: 0;
211
+ box-sizing: border-box;
212
+ }
213
+
214
+ body {
215
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
216
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
217
+ min-height: 100vh;
218
+ display: flex;
219
+ align-items: center;
220
+ justify-content: center;
221
+ }
222
+
223
+ .app {
224
+ background: white;
225
+ padding: 3rem 4rem;
226
+ border-radius: 1rem;
227
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
228
+ text-align: center;
229
+ }
230
+
231
+ h1 {
232
+ color: #1a202c;
233
+ margin-bottom: 0.5rem;
234
+ }
235
+
236
+ p {
237
+ color: #718096;
238
+ }
239
+ `);
240
+ }
171
241
 
172
- spinner.succeed('Project created successfully!');
242
+ spinner.succeed('Project created successfully!');
173
243
 
174
- console.log(chalk.green(`
244
+ console.log(chalk.green(`
175
245
  ✅ Project "${name}" created!
176
246
 
177
247
  Next steps:
@@ -181,33 +251,33 @@ Next steps:
181
251
 
182
252
  `));
183
253
 
184
- // Check for required dependencies
185
- await checkDependencies(answers);
254
+ // Check for required dependencies
255
+ await checkDependencies(answers);
186
256
 
187
- } catch (error) {
188
- spinner.fail('Failed to create project');
189
- console.error(chalk.red(error.message));
190
- process.exit(1);
191
- }
257
+ } catch (error) {
258
+ spinner.fail('Failed to create project');
259
+ console.error(chalk.red(error.message));
260
+ process.exit(1);
261
+ }
192
262
  }
193
263
 
194
264
  async function checkDependencies(answers) {
195
- const hasDesktop = answers.platforms?.some(p => ['windows', 'macos', 'linux'].includes(p));
265
+ const hasDesktop = answers.platforms?.some(p => ['windows', 'macos', 'linux'].includes(p));
196
266
 
197
- if (hasDesktop && answers.desktopFramework === 'tauri') {
198
- console.log(chalk.yellow('\n⚠️ Tauri Requirements:'));
199
- console.log(chalk.gray(' Run "slct build desktop" for detailed setup instructions.\n'));
200
- }
267
+ if (hasDesktop && answers.desktopFramework === 'tauri') {
268
+ console.log(chalk.yellow('\n⚠️ Tauri Requirements:'));
269
+ console.log(chalk.gray(' Run "slct build desktop" for detailed setup instructions.\n'));
270
+ }
201
271
  }
202
272
 
203
273
  async function createGithubWorkflow(projectDir, framework, name) {
204
- const workflowDir = path.join(projectDir, '.github', 'workflows');
205
- await fs.ensureDir(workflowDir);
274
+ const workflowDir = path.join(projectDir, '.github', 'workflows');
275
+ await fs.ensureDir(workflowDir);
206
276
 
207
- let workflowContent = '';
277
+ let workflowContent = '';
208
278
 
209
- if (framework === 'tauri') {
210
- workflowContent = `name: Build Apps
279
+ if (framework === 'tauri') {
280
+ workflowContent = `name: Build Apps
211
281
  on:
212
282
  push:
213
283
  branches: [ main ]
@@ -253,9 +323,9 @@ jobs:
253
323
  releaseDraft: true
254
324
  prerelease: false
255
325
  `;
256
- } else {
257
- // Electron Workflow
258
- workflowContent = `name: Build Apps
326
+ } else {
327
+ // Electron Workflow
328
+ workflowContent = `name: Build Apps
259
329
  on:
260
330
  push:
261
331
  branches: [ main ]
@@ -287,9 +357,9 @@ jobs:
287
357
  env:
288
358
  GH_TOKEN: \${{ secrets.GITHUB_TOKEN }}
289
359
  `;
290
- }
360
+ }
291
361
 
292
- await fs.writeFile(path.join(workflowDir, 'build.yml'), workflowContent);
362
+ await fs.writeFile(path.join(workflowDir, 'build.yml'), workflowContent);
293
363
  }
294
364
 
295
365
  module.exports = init;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iselect/select",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Build and deploy apps for web, desktop, and mobile from a single codebase",
5
5
  "main": "lib/index.js",
6
6
  "bin": {