gladvn 0.2.29 → 0.2.31

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/bin/cli.js +228 -134
  2. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -6,14 +6,29 @@ import { execSync } from 'child_process';
6
6
  import readline from 'readline';
7
7
 
8
8
  const cyan = "\x1b[36m";
9
- const green = "\x1b[32m";
10
9
  const yellow = "\x1b[33m";
11
10
  const bold = "\x1b[1m";
12
11
  const dim = "\x1b[90m";
13
12
  const reset = "\x1b[0m";
14
13
 
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = path.dirname(__filename);
16
+
17
+ // Read version from package.json
18
+ const pkgPath = path.resolve(__dirname, "../package.json");
19
+ const pkgContent = fs.readFileSync(pkgPath, "utf-8");
20
+ const pkg = JSON.parse(pkgContent);
21
+ const VERSION = pkg.version;
22
+
23
+ if (process.argv.includes('--version') || process.argv.includes('-v')) {
24
+ console.log(`gladvn v${VERSION}`);
25
+ process.exit(0);
26
+ }
27
+
15
28
  if (process.argv.includes('--help') || process.argv.includes('-h')) {
16
29
  console.log(`
30
+ gladvn v${VERSION}
31
+
17
32
  Usage:
18
33
  npx gladvn init [destination]
19
34
  npx gladvn add <component | --all> [destination]
@@ -22,6 +37,7 @@ Usage:
22
37
  Options:
23
38
  [destination] The folder where components will be copied. Defaults to "gladvn".
24
39
  --help, -h Show this help message.
40
+ --version, -v Show the current version.
25
41
  `);
26
42
  process.exit(0);
27
43
  }
@@ -54,13 +70,15 @@ if (args[0] === "init") {
54
70
  userDest = args[0];
55
71
  }
56
72
 
57
- const __filename = fileURLToPath(import.meta.url);
58
- const __dirname = path.dirname(__filename);
59
-
60
73
  const destPath = path.resolve(process.cwd(), userDest);
61
74
  const srcDir = path.resolve(__dirname, "../src");
62
75
 
63
- // Recursive file scanner
76
+ // Helper: check if a file is a test file
77
+ function isTestFile(filename) {
78
+ return /\.test\.(tsx?|jsx?)$/.test(filename);
79
+ }
80
+
81
+ // Recursive file scanner for CSS files
64
82
  const IGNORE_DIRS = ['node_modules', '.git', '.next', 'dist', 'build', 'out', 'coverage', '.cache', userDest];
65
83
 
66
84
  function findCssFiles(dir, fileList = []) {
@@ -83,6 +101,23 @@ function findCssFiles(dir, fileList = []) {
83
101
  return fileList;
84
102
  }
85
103
 
104
+ // Recursive directory copy with optional filter
105
+ function copyDirFiltered(src, dest, filter) {
106
+ if (!fs.existsSync(src)) return;
107
+ if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
108
+ const entries = fs.readdirSync(src, { withFileTypes: true });
109
+ for (const entry of entries) {
110
+ const srcPath = path.join(src, entry.name);
111
+ const destPath = path.join(dest, entry.name);
112
+ if (entry.isDirectory()) {
113
+ copyDirFiltered(srcPath, destPath, filter);
114
+ } else {
115
+ if (filter && !filter(entry.name)) continue;
116
+ fs.cpSync(srcPath, destPath, { force: true });
117
+ }
118
+ }
119
+ }
120
+
86
121
  // Native arrow key selector
87
122
  async function selectOption(message, options) {
88
123
  return new Promise((resolve) => {
@@ -141,82 +176,33 @@ async function selectOption(message, options) {
141
176
  });
142
177
  }
143
178
 
144
- async function selectMultipleOptions(message, options) {
145
- return new Promise((resolve) => {
146
- let selectedIndex = 0;
147
- const selected = new Set();
148
-
149
- const render = () => {
150
- process.stdout.write('\x1B[?25l');
151
- readline.cursorTo(process.stdout, 0);
152
- readline.clearScreenDown(process.stdout);
153
-
154
- console.log(`\x1b[33m?\x1b[0m \x1b[1m${message}\x1b[0m \x1b[90m(Press <space> to select, <enter> to confirm)\x1b[0m`);
155
- options.forEach((opt, index) => {
156
- const isChecked = selected.has(index);
157
- const checkbox = isChecked ? '\x1b[32m◉\x1b[0m' : '\x1b[90m◯\x1b[0m';
158
- if (index === selectedIndex) {
159
- console.log(` \x1b[36m❯ ${checkbox} ${opt}\x1b[0m`);
160
- } else {
161
- console.log(` ${checkbox} ${opt}`);
162
- }
163
- });
164
- readline.moveCursor(process.stdout, 0, -(options.length + 1));
165
- };
166
-
167
- render();
168
-
169
- const onKeyPress = (str, key) => {
170
- if (key.name === 'up') {
171
- selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : options.length - 1;
172
- render();
173
- } else if (key.name === 'down') {
174
- selectedIndex = selectedIndex < options.length - 1 ? selectedIndex + 1 : 0;
175
- render();
176
- } else if (key.name === 'space') {
177
- if (selected.has(selectedIndex)) {
178
- selected.delete(selectedIndex);
179
- } else {
180
- selected.add(selectedIndex);
181
- }
182
- render();
183
- } else if (key.name === 'return' || key.name === 'enter') {
184
- cleanup();
185
- const results = Array.from(selected).map(idx => options[idx]);
186
- console.log(`\x1b[32m✔\x1b[0m \x1b[1m${message}\x1b[0m \x1b[90m…\x1b[0m \x1b[36m${results.length} selected\x1b[0m`);
187
- resolve(results);
188
- } else if (key.name === 'c' && key.ctrl) {
189
- cleanup();
190
- console.log('\n\x1b[31m✖ Cancelled.\x1b[0m');
191
- process.exit(1);
179
+ // Helper: ensure lib/ and hooks/ exist in destPath (needed for add/add-block)
180
+ function ensureCoreDeps(srcDir, destPath) {
181
+ const coreDeps = ['lib', 'hooks'];
182
+ let copied = false;
183
+ for (const dir of coreDeps) {
184
+ const targetDir = path.join(destPath, dir);
185
+ if (!fs.existsSync(targetDir)) {
186
+ const sourceDir = path.join(srcDir, dir);
187
+ if (fs.existsSync(sourceDir)) {
188
+ copyDirFiltered(sourceDir, targetDir, (name) => !isTestFile(name));
189
+ copied = true;
192
190
  }
193
- };
194
-
195
- const cleanup = () => {
196
- process.stdin.removeListener('keypress', onKeyPress);
197
- if (process.stdin.isTTY) process.stdin.setRawMode(false);
198
- process.stdin.pause();
199
- readline.moveCursor(process.stdout, 0, options.length + 1);
200
- process.stdout.write('\x1B[?25h');
201
- readline.moveCursor(process.stdout, 0, -(options.length + 1));
202
- readline.clearScreenDown(process.stdout);
203
- };
204
-
205
- readline.emitKeypressEvents(process.stdin);
206
- if (process.stdin.isTTY) process.stdin.setRawMode(true);
207
- process.stdin.resume();
208
- process.stdin.on('keypress', onKeyPress);
209
- });
191
+ }
192
+ }
193
+ if (copied) {
194
+ console.log(`\x1b[32m✔ Copied required core dependencies (lib, hooks)\x1b[0m`);
195
+ }
210
196
  }
211
197
 
212
198
  async function main() {
213
199
  console.log(`\n${cyan}╔══════════════════════════════════════════════════════════════════╗${reset}`);
214
200
  if (command === "add") {
215
- console.log(`${cyan}║ ${bold}gladvn${reset}${cyan} — Add Component ║${reset}`);
201
+ console.log(`${cyan}║ ${bold}gladvn${reset} ${dim}v${VERSION}${reset}${cyan} — Add Component ║${reset}`);
216
202
  } else if (command === "add-block") {
217
- console.log(`${cyan}║ ${bold}gladvn${reset}${cyan} — Add Block ║${reset}`);
203
+ console.log(`${cyan}║ ${bold}gladvn${reset} ${dim}v${VERSION}${reset}${cyan} — Add Block ║${reset}`);
218
204
  } else {
219
- console.log(`${cyan}║ ${bold}gladvn${reset}${cyan} — Initialization ║${reset}`);
205
+ console.log(`${cyan}║ ${bold}gladvn${reset} ${dim}v${VERSION}${reset}${cyan} — Initialization ║${reset}`);
220
206
  }
221
207
  console.log(`${cyan}╚══════════════════════════════════════════════════════════════════╝${reset}\n`);
222
208
 
@@ -233,7 +219,7 @@ async function main() {
233
219
  if (fs.existsSync(subPath)) {
234
220
  const files = fs.readdirSync(subPath);
235
221
  for (const file of files) {
236
- if (file.endsWith('.tsx') || file.endsWith('.ts')) {
222
+ if ((file.endsWith('.tsx') || file.endsWith('.ts')) && !isTestFile(file)) {
237
223
  availableComponents.push(`${sub}/${file}`);
238
224
  }
239
225
  }
@@ -247,17 +233,21 @@ async function main() {
247
233
  if (fs.existsSync(blocksDir)) {
248
234
  const files = fs.readdirSync(blocksDir);
249
235
  for (const file of files) {
250
- if (file.endsWith('.tsx') || file.endsWith('.ts')) {
236
+ if ((file.endsWith('.tsx') || file.endsWith('.ts')) && !isTestFile(file)) {
251
237
  availableBlocks.push(file);
252
238
  }
253
239
  }
254
240
  }
255
241
 
256
242
  if (command === "add") {
243
+ // ── ADD COMMAND ──────────────────────────────────────────────────────
257
244
  if (!fs.existsSync(destPath)) {
258
245
  fs.mkdirSync(destPath, { recursive: true });
259
246
  }
260
247
 
248
+ // Ensure lib/ and hooks/ exist for component imports
249
+ ensureCoreDeps(srcDir, destPath);
250
+
261
251
  if (componentToAdd === "--all") {
262
252
  console.log(`\x1b[36mAdding all components to ${userDest}...\x1b[0m`);
263
253
  let addedCount = 0;
@@ -301,7 +291,9 @@ async function main() {
301
291
  hasErrors = true;
302
292
  }
303
293
  }
294
+
304
295
  } else if (command === "add-block") {
296
+ // ── ADD-BLOCK COMMAND ────────────────────────────────────────────────
305
297
  console.log(`\x1b[36mAdding block ${componentToAdd} to ${userDest}...\x1b[0m`);
306
298
  const blockMatches = availableBlocks.filter(b => b === `${componentToAdd}.tsx` || b === `${componentToAdd}.ts`);
307
299
  if (blockMatches.length === 0) {
@@ -312,6 +304,10 @@ async function main() {
312
304
  if (!fs.existsSync(destPath)) {
313
305
  fs.mkdirSync(destPath, { recursive: true });
314
306
  }
307
+
308
+ // Ensure lib/ and hooks/ exist for block imports
309
+ ensureCoreDeps(srcDir, destPath);
310
+
315
311
  const block = blockMatches[0];
316
312
  const sourcePath = path.join(blocksDir, block);
317
313
  const targetPath = path.join(destPath, 'blocks', block);
@@ -326,8 +322,9 @@ async function main() {
326
322
  console.error(`\x1b[31m✖ Failed to add block ${block}: ${err.message}\x1b[0m`);
327
323
  hasErrors = true;
328
324
  }
325
+
329
326
  } else {
330
- // === INIT COMMAND ===
327
+ // ── INIT COMMAND ─────────────────────────────────────────────────────
331
328
  let targetCss = "app/globals.css";
332
329
  const cssFilesFull = findCssFiles(process.cwd());
333
330
  const cssFiles = cssFilesFull.map(f => path.relative(process.cwd(), f));
@@ -376,80 +373,177 @@ async function main() {
376
373
  cssFilePath = path.resolve(process.cwd(), targetCss);
377
374
  }
378
375
 
379
- // 1. Copy files
376
+ // Step 1. Copy files
380
377
  console.log(`\n\x1b[36mInitializing gladvn components into ${userDest}...\x1b[0m`);
381
378
 
382
379
  if (!fs.existsSync(destPath)) {
383
380
  fs.mkdirSync(destPath, { recursive: true });
384
381
  }
385
382
 
386
- // Always copy core directories
383
+ // Copy core directories (excluding test files)
384
+ const coreDirs = ['hooks', 'lib', 'styles', 'components', 'blocks'];
385
+ for (const dir of coreDirs) {
386
+ const sourceDir = path.join(srcDir, dir);
387
+ const targetDir = path.join(destPath, dir);
388
+ if (fs.existsSync(sourceDir)) {
389
+ try {
390
+ copyDirFiltered(sourceDir, targetDir, (name) => !isTestFile(name));
391
+ } catch (err) {
392
+ console.error(`\x1b[31m✖ Failed to copy core ${dir}/: ${err.message}\x1b[0m`);
393
+ hasErrors = true;
394
+ }
395
+ }
396
+ }
387
397
 
388
- const coreDirs = ['hooks', 'lib', 'styles', 'components', 'blocks'];
389
- for (const dir of coreDirs) {
390
- const sourcePath = path.join(srcDir, dir);
391
- const targetPath = path.join(destPath, dir);
392
- if (fs.existsSync(sourcePath)) {
393
- try {
394
- fs.cpSync(sourcePath, targetPath, { recursive: true, force: true });
395
- } catch (err) {
396
- console.error(`\x1b[31m✖ Failed to copy core ${dir}/: ${err.message}\x1b[0m`);
397
- hasErrors = true;
398
+ // Copy root-level files (index.ts, preset.ts)
399
+ const rootFiles = ['index.ts', 'preset.ts'];
400
+ for (const file of rootFiles) {
401
+ const sourceFile = path.join(srcDir, file);
402
+ const targetFile = path.join(destPath, file);
403
+ if (fs.existsSync(sourceFile)) {
404
+ try {
405
+ fs.cpSync(sourceFile, targetFile, { force: true });
406
+ } catch (err) {
407
+ console.error(`\x1b[31m✖ Failed to copy ${file}: ${err.message}\x1b[0m`);
408
+ hasErrors = true;
409
+ }
398
410
  }
399
411
  }
400
- }
401
- console.log(`\x1b[32m✔ Copied core files and all components (lib, hooks, styles, components, blocks)\x1b[0m`);
402
-
403
- // 2. Inject CSS
404
- if (cssFilePath && fs.existsSync(cssFilePath)) {
405
- const cssContent = fs.readFileSync(cssFilePath, 'utf8');
406
- const cssTarget = path.join(destPath, 'styles', 'gladvn.css');
407
- let relPath = path.relative(path.dirname(cssFilePath), cssTarget).replace(/\\/g, '/');
408
- if (!relPath.startsWith('.')) relPath = './' + relPath;
409
- const importStmt = `@import "${relPath}";`;
410
- if (!cssContent.includes(importStmt) && !cssContent.includes('gladvn.css')) {
411
- if (cssContent.includes('@import "tailwindcss";')) {
412
- fs.writeFileSync(cssFilePath, cssContent.replace('@import "tailwindcss";', `@import "tailwindcss";\n${importStmt}`));
413
- } else if (cssContent.includes('@tailwind base;')) {
414
- fs.writeFileSync(cssFilePath, cssContent.replace('@tailwind base;', `@tailwind base;\n${importStmt}`));
415
- } else {
416
- fs.writeFileSync(cssFilePath, `${importStmt}\n${cssContent}`);
412
+
413
+ console.log(`\x1b[32m✔ Copied core files and all components (lib, hooks, styles, components, blocks)\x1b[0m`);
414
+
415
+ // Step 2. Inject CSS (tokens.css MUST come before gladvn.css)
416
+ if (cssFilePath && fs.existsSync(cssFilePath)) {
417
+ let cssContent = fs.readFileSync(cssFilePath, 'utf8');
418
+ const stylesDir = path.join(destPath, 'styles');
419
+ const relDir = path.relative(path.dirname(cssFilePath), stylesDir).replace(/\\/g, '/');
420
+ const prefix = relDir.startsWith('.') ? relDir : './' + relDir;
421
+
422
+ const tokensImport = `@import "${prefix}/tokens.css";`;
423
+ const gladvnImport = `@import "${prefix}/gladvn.css";`;
424
+ const combinedImport = `${tokensImport}\n${gladvnImport}`;
425
+
426
+ const needsTokens = !cssContent.includes('tokens.css');
427
+ const needsGladvn = !cssContent.includes('gladvn.css');
428
+
429
+ if (needsTokens || needsGladvn) {
430
+ const toInject = needsTokens && needsGladvn
431
+ ? combinedImport
432
+ : needsTokens ? tokensImport : gladvnImport;
433
+
434
+ if (cssContent.includes('@import "tailwindcss";')) {
435
+ cssContent = cssContent.replace('@import "tailwindcss";', `@import "tailwindcss";\n${toInject}`);
436
+ } else if (cssContent.includes('@tailwind base;')) {
437
+ cssContent = cssContent.replace('@tailwind base;', `@tailwind base;\n${toInject}`);
438
+ } else {
439
+ cssContent = `${toInject}\n${cssContent}`;
440
+ }
441
+ fs.writeFileSync(cssFilePath, cssContent);
442
+ console.log(`\x1b[32m✔ Injected gladvn styles (tokens + theme) into ${path.basename(cssFilePath)}\x1b[0m`);
417
443
  }
418
- console.log(`\x1b[32m✔ Injected gladvn CSS into ${path.basename(cssFilePath)}\x1b[0m`);
419
444
  }
420
- }
421
445
 
422
- // 3. Configure Path Alias
423
- let tsconfigPath = path.join(process.cwd(), 'tsconfig.json');
424
- if (!fs.existsSync(tsconfigPath)) tsconfigPath = path.join(process.cwd(), 'jsconfig.json');
425
-
426
- if (fs.existsSync(tsconfigPath)) {
427
- let content = fs.readFileSync(tsconfigPath, 'utf8');
428
- if (!content.includes('@gladvn/*')) {
429
- const pathsEmptyRegex = /"paths"\s*:\s*\{\s*\}/;
430
- const compilerOptionsEmptyRegex = /"compilerOptions"\s*:\s*\{\s*\}/;
431
-
432
- if (pathsEmptyRegex.test(content)) {
433
- content = content.replace(pathsEmptyRegex, `"paths": {\n "@gladvn/*": ["./${userDest}/*"]\n }`);
434
- } else if (content.match(/"paths"\s*:\s*\{/)) {
435
- content = content.replace(/"paths"\s*:\s*\{/, `"paths": {\n "@gladvn/*": ["./${userDest}/*"],`);
436
- } else if (compilerOptionsEmptyRegex.test(content)) {
437
- content = content.replace(compilerOptionsEmptyRegex, `"compilerOptions": {\n "paths": {\n "@gladvn/*": ["./${userDest}/*"]\n }\n }`);
438
- } else if (content.match(/"compilerOptions"\s*:\s*\{/)) {
439
- content = content.replace(/"compilerOptions"\s*:\s*\{/, `"compilerOptions": {\n "paths": {\n "@gladvn/*": ["./${userDest}/*"]\n },`);
446
+ // Step 3. Configure TypeScript Path Alias
447
+ const tsconfigFiles = ['tsconfig.app.json', 'tsconfig.json', 'jsconfig.json'];
448
+ let tsconfigPath = null;
449
+ for (const file of tsconfigFiles) {
450
+ const p = path.join(process.cwd(), file);
451
+ if (fs.existsSync(p)) {
452
+ tsconfigPath = p;
453
+ break;
454
+ }
455
+ }
456
+
457
+ if (tsconfigPath) {
458
+ let content = fs.readFileSync(tsconfigPath, 'utf8');
459
+ if (!content.includes('@gladvn/*')) {
460
+ const pathsEmptyRegex = /"paths"\s*:\s*\{\s*\}/;
461
+ const compilerOptionsEmptyRegex = /"compilerOptions"\s*:\s*\{\s*\}/;
462
+
463
+ if (pathsEmptyRegex.test(content)) {
464
+ content = content.replace(pathsEmptyRegex, `"paths": {\n "@gladvn/*": ["./${userDest}/*"]\n }`);
465
+ } else if (content.match(/"paths"\s*:\s*\{/)) {
466
+ content = content.replace(/"paths"\s*:\s*\{/, `"paths": {\n "@gladvn/*": ["./${userDest}/*"],`);
467
+ } else if (compilerOptionsEmptyRegex.test(content)) {
468
+ content = content.replace(compilerOptionsEmptyRegex, `"compilerOptions": {\n "paths": {\n "@gladvn/*": ["./${userDest}/*"]\n }\n }`);
469
+ } else if (content.match(/"compilerOptions"\s*:\s*\{/)) {
470
+ content = content.replace(/"compilerOptions"\s*:\s*\{/, `"compilerOptions": {\n "paths": {\n "@gladvn/*": ["./${userDest}/*"]\n },`);
471
+ } else if (!content.includes('"compilerOptions"')) {
472
+ content = content.replace(/\{/, `{\n "compilerOptions": {\n "paths": {\n "@gladvn/*": ["./${userDest}/*"]\n }\n },`);
473
+ }
474
+ fs.writeFileSync(tsconfigPath, content);
475
+ console.log(`\x1b[32m✔ Configured path alias @gladvn/* in ${path.basename(tsconfigPath)}\x1b[0m`);
476
+ }
477
+ }
478
+
479
+ // Step 4. Configure Vite Alias
480
+ const viteConfigFiles = ['vite.config.ts', 'vite.config.js', 'vite.config.mts', 'vite.config.mjs'];
481
+ let viteConfigPath = null;
482
+ for (const file of viteConfigFiles) {
483
+ const p = path.join(process.cwd(), file);
484
+ if (fs.existsSync(p)) {
485
+ viteConfigPath = p;
486
+ break;
487
+ }
488
+ }
489
+
490
+ if (viteConfigPath) {
491
+ let content = fs.readFileSync(viteConfigPath, 'utf8');
492
+ if (!content.includes('@gladvn')) {
493
+ // Add `import path from "path"` if missing
494
+ if (!content.includes('import path from') && !content.includes('require("path")')) {
495
+ if (content.includes('import ')) {
496
+ const lastImportIndex = content.lastIndexOf('import ');
497
+ const endOfLine = content.indexOf('\n', lastImportIndex);
498
+ if (endOfLine !== -1) {
499
+ content = content.slice(0, endOfLine + 1) + 'import path from "path"\n' + content.slice(endOfLine + 1);
500
+ } else {
501
+ content = 'import path from "path"\n' + content;
502
+ }
503
+ } else {
504
+ content = 'import path from "path"\n' + content;
505
+ }
506
+ }
507
+
508
+ const resolveAlias = `\n resolve: {\n alias: {\n "@gladvn": path.resolve(__dirname, "./${userDest}"),\n },\n },`;
509
+
510
+ if (content.includes('resolve: {')) {
511
+ if (content.includes('alias: {')) {
512
+ content = content.replace(/alias:\s*\{/, `alias: {\n "@gladvn": path.resolve(__dirname, "./${userDest}"),`);
513
+ } else {
514
+ content = content.replace(/resolve:\s*\{/, `resolve: {\n alias: {\n "@gladvn": path.resolve(__dirname, "./${userDest}"),\n },`);
515
+ }
516
+ } else {
517
+ content = content.replace(/defineConfig\s*\(\s*\{/, `defineConfig({${resolveAlias}`);
518
+ }
519
+
520
+ fs.writeFileSync(viteConfigPath, content);
521
+ console.log(`\x1b[32m✔ Configured resolve.alias in ${path.basename(viteConfigPath)}\x1b[0m`);
440
522
  }
441
- fs.writeFileSync(tsconfigPath, content);
442
- console.log(`\x1b[32m✔ Configured path alias @gladvn/* in ${path.basename(tsconfigPath)}\x1b[0m`);
443
523
  }
444
- }
445
524
 
446
525
  } // END OF INIT COMMAND
447
526
 
448
- // 3. Install dependencies
527
+ // Step 5. Install dependencies
528
+ // Track extra deps needed (e.g. @types/node for Vite TS projects)
529
+ const extraDepsToInstall = [];
530
+
531
+ // Check if we need @types/node (Vite + TypeScript)
532
+ const viteConfigExists = ['vite.config.ts', 'vite.config.mts'].some(f => fs.existsSync(path.join(process.cwd(), f)));
533
+ if (viteConfigExists) {
534
+ try {
535
+ const userPkgPath = path.resolve(process.cwd(), 'package.json');
536
+ if (fs.existsSync(userPkgPath)) {
537
+ const userPkg = JSON.parse(fs.readFileSync(userPkgPath, 'utf8'));
538
+ const allUserDeps = { ...userPkg.dependencies, ...userPkg.devDependencies };
539
+ if (!allUserDeps['@types/node']) {
540
+ extraDepsToInstall.push('@types/node@"^20.0.0"');
541
+ }
542
+ }
543
+ } catch (e) {}
544
+ }
545
+
449
546
  try {
450
- const pkgPath = path.resolve(__dirname, "../package.json");
451
- const pkgContent = fs.readFileSync(pkgPath, "utf-8");
452
- const pkg = JSON.parse(pkgContent);
453
547
  const depsObj = pkg.dependencies || {};
454
548
 
455
549
  if (Object.keys(depsObj).length > 0) {
@@ -489,7 +583,7 @@ async function main() {
489
583
  }
490
584
  }
491
585
 
492
- if (usedDeps.size > 0) {
586
+ if (usedDeps.size > 0 || extraDepsToInstall.length > 0) {
493
587
  // Check user's package.json to see what is already installed
494
588
  const userPkgPath = path.resolve(process.cwd(), 'package.json');
495
589
  let userDeps = {};
@@ -500,7 +594,7 @@ async function main() {
500
594
  } catch (e) {}
501
595
  }
502
596
 
503
- const depsToInstall = [];
597
+ const depsToInstall = [...extraDepsToInstall];
504
598
  for (const dep of usedDeps) {
505
599
  if (!userDeps[dep]) {
506
600
  depsToInstall.push(`${dep}@"${depsObj[dep]}"`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gladvn",
3
- "version": "0.2.29",
3
+ "version": "0.2.31",
4
4
  "type": "module",
5
5
  "packageManager": "pnpm@10.11.0",
6
6
  "description": "A CLI to scaffold beautiful, accessible React components into your project. Powered by Tailwind CSS v4 and Base UI.",