package-installer-cli 1.2.0 → 1.3.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.
@@ -3,11 +3,12 @@ import { promisify } from 'util';
3
3
  import chalk from 'chalk';
4
4
  import ora from 'ora';
5
5
  import boxen from 'boxen';
6
- import gradient from 'gradient-string';
7
6
  import fs from 'fs-extra';
8
7
  import path from 'path';
9
8
  import semver from 'semver';
10
9
  import https from 'https';
10
+ import { createStandardHelp } from '../utils/helpFormatter.js';
11
+ import { displayCommandBanner } from '../utils/banner.js';
11
12
  import { getSupportedLanguages, getLanguageConfig } from '../utils/languageConfig.js';
12
13
  const execAsync = promisify(exec);
13
14
  // Generate PROJECT_TYPES from shared language configuration with enhanced registry support
@@ -18,7 +19,6 @@ const PROJECT_TYPES = getSupportedLanguages().map(lang => {
18
19
  let registryUrl;
19
20
  let packageInfoUrl;
20
21
  switch (lang) {
21
- case 'nodejs':
22
22
  case 'javascript':
23
23
  case 'typescript':
24
24
  registryUrl = 'https://registry.npmjs.org';
@@ -48,7 +48,8 @@ const PROJECT_TYPES = getSupportedLanguages().map(lang => {
48
48
  const deps = {};
49
49
  // Language-specific dependency parsing
50
50
  switch (lang) {
51
- case 'nodejs':
51
+ case 'javascript':
52
+ case 'typescript':
52
53
  if (filename === 'package.json' || filename === 'package-lock.json' || filename === 'pnpm-lock.yml') {
53
54
  return {
54
55
  ...content.dependencies,
@@ -104,14 +105,6 @@ const PROJECT_TYPES = getSupportedLanguages().map(lang => {
104
105
  }
105
106
  }
106
107
  break;
107
- case 'php':
108
- if (filename === 'composer.json') {
109
- return {
110
- ...content.require,
111
- ...content['require-dev']
112
- };
113
- }
114
- break;
115
108
  case 'ruby':
116
109
  if (filename === 'Gemfile') {
117
110
  const lines = content.toString().split('\n');
@@ -159,16 +152,13 @@ const PROJECT_TYPES = getSupportedLanguages().map(lang => {
159
152
  });
160
153
  function getRegistryUrl(lang) {
161
154
  switch (lang) {
162
- case 'nodejs': return 'https://registry.npmjs.org';
155
+ case 'javascript':
156
+ case 'typescript':
157
+ return 'https://registry.npmjs.org';
163
158
  case 'rust': return 'https://crates.io/api/v1/crates';
164
159
  case 'python': return 'https://pypi.org/pypi';
165
160
  case 'go': return 'https://proxy.golang.org';
166
161
  case 'ruby': return 'https://rubygems.org/api/v1/gems';
167
- case 'php': return 'https://packagist.org/packages';
168
- case 'java': return 'https://repo1.maven.org/maven2';
169
- case 'csharp': return 'https://api.nuget.org/v3-flatcontainer';
170
- case 'swift': return 'https://packagecatalog.com';
171
- case 'dart': return 'https://pub.dev/api/packages';
172
162
  default: return '';
173
163
  }
174
164
  }
@@ -191,7 +181,8 @@ async function fetchPackageFromRegistry(packageName, projectType) {
191
181
  const parsed = JSON.parse(data);
192
182
  // Handle different registry response formats
193
183
  switch (projectType.name) {
194
- case 'Node.js':
184
+ case 'JavaScript':
185
+ case 'Typescript':
195
186
  resolve(parseNpmRegistryResponse(parsed, packageName));
196
187
  break;
197
188
  case 'Rust':
@@ -293,7 +284,8 @@ function parseGenericResponse(data, packageName) {
293
284
  async function getLatestVersion(packageName, projectType) {
294
285
  try {
295
286
  switch (projectType.name) {
296
- case 'Node.js':
287
+ case 'JavaScript':
288
+ case 'TypeScript':
297
289
  const { stdout } = await execAsync(`npm view ${packageName} version`);
298
290
  return stdout.trim();
299
291
  case 'Rust':
@@ -380,33 +372,42 @@ async function getEnhancedPackageInfo(name, currentVersion, projectType) {
380
372
  * Display help for check command
381
373
  */
382
374
  export function showCheckHelp() {
383
- const piGradient = gradient(['#00c6ff', '#0072ff']);
384
- const headerGradient = gradient(['#4facfe', '#00f2fe']);
385
- console.log('\n' + boxen(headerGradient('🔍 Check Command Help') + '\n\n' +
386
- chalk.white('Check package versions in your project and get suggestions for updates.') + '\n' +
387
- chalk.white('Helps you keep your dependencies up-to-date and secure.') + '\n\n' +
388
- chalk.cyan('Usage:') + '\n' +
389
- chalk.white(` ${piGradient('pi')} ${chalk.hex('#f39c12')('check')} [package-name] [options]`) + '\n\n' +
390
- chalk.cyan('Options:') + '\n' +
391
- chalk.gray(' -h, --help Display help for this command') + '\n' +
392
- chalk.gray(' -v, --verbose Show detailed information for all packages') + '\n\n' +
393
- chalk.cyan('Examples:') + '\n' +
394
- chalk.gray(` ${piGradient('pi')} ${chalk.hex('#f39c12')('check')} # Check all packages in current project`) + '\n' +
395
- chalk.gray(` ${piGradient('pi')} ${chalk.hex('#f39c12')('check')} ${chalk.hex('#feca57')('--verbose')} # Check all packages with detailed info`) + '\n' +
396
- chalk.gray(` ${piGradient('pi')} ${chalk.hex('#f39c12')('check')} react # Check specific package version`) + '\n' +
397
- chalk.gray(` ${piGradient('pi')} ${chalk.hex('#f39c12')('check')} @types/node # Check scoped packages`) + '\n' +
398
- chalk.gray(` ${piGradient('pi')} ${chalk.hex('#f39c12')('check')} ${chalk.hex('#ff6b6b')('--help')} # Show this help message`) + '\n\n' +
399
- chalk.hex('#00d2d3')('💡 Supported Package Managers:') + '\n' +
400
- chalk.hex('#95afc0')(' • npm, pnpm, yarn (Node.js)') + '\n' +
401
- chalk.hex('#95afc0')(' • pip, pipenv, poetry (Python)') + '\n' +
402
- chalk.hex('#95afc0')(' • cargo (Rust)') + '\n' +
403
- chalk.hex('#95afc0')(' • go modules (Go)') + '\n' +
404
- chalk.hex('#95afc0')(' • composer (PHP)'), {
405
- padding: 1,
406
- borderStyle: 'round',
407
- borderColor: 'cyan',
408
- backgroundColor: '#0a0a0a'
409
- }));
375
+ const helpConfig = {
376
+ commandName: 'Check',
377
+ emoji: '🔍',
378
+ description: 'Check package versions in your project and get suggestions for updates.\nHelps you keep your dependencies up-to-date and secure.',
379
+ usage: [
380
+ 'check [package-name] [options]',
381
+ 'check [options]'
382
+ ],
383
+ options: [
384
+ { flag: '-v, --verbose', description: 'Show detailed information for all packages' }
385
+ ],
386
+ examples: [
387
+ { command: 'check', description: 'Check all packages in current project' },
388
+ { command: 'check --verbose', description: 'Check all packages with detailed info' },
389
+ { command: 'check react', description: 'Check specific package version' },
390
+ { command: 'check @types/node', description: 'Check scoped packages' },
391
+ { command: 'check --help', description: 'Show this help message' }
392
+ ],
393
+ additionalSections: [
394
+ {
395
+ title: 'Supported Package Managers',
396
+ items: [
397
+ 'npm, pnpm, yarn (Node.js)',
398
+ 'pip, pipenv, poetry (Python)',
399
+ 'cargo (Rust)',
400
+ 'go modules (Go)',
401
+ 'composer (PHP)'
402
+ ]
403
+ }
404
+ ],
405
+ tips: [
406
+ 'Use --verbose for detailed package information including security vulnerabilities',
407
+ 'Check specific packages by name for targeted updates'
408
+ ]
409
+ };
410
+ createStandardHelp(helpConfig);
410
411
  }
411
412
  export async function checkCommand(packageName, options) {
412
413
  // Check for help flag
@@ -426,6 +427,8 @@ export async function checkCommand(packageName, options) {
426
427
  packageName = undefined;
427
428
  }
428
429
  try {
430
+ // Display command banner
431
+ displayCommandBanner('Check', 'Check package versions and updates for your project dependencies');
429
432
  console.log('\n' + chalk.hex('#f39c12')('🔍 Starting package check...'));
430
433
  if (packageName && packageName !== '--verbose' && packageName !== '-v') {
431
434
  await checkSinglePackage(packageName, isVerbose);
@@ -500,61 +503,100 @@ async function checkProjectPackages(verbose = false) {
500
503
  }
501
504
  async function detectProjectType() {
502
505
  console.log(chalk.gray('🔍 Detecting project type...'));
506
+ // Priority order for detection - check most common files first
507
+ const priorityFiles = ['package.json', 'Cargo.toml', 'requirements.txt', 'composer.json', 'go.mod'];
508
+ // First pass: check priority files in current directory
509
+ for (const priorityFile of priorityFiles) {
510
+ const filePath = path.join(process.cwd(), priorityFile);
511
+ console.log(chalk.gray(` Checking priority file: ${filePath}`));
512
+ if (await fs.pathExists(filePath)) {
513
+ console.log(chalk.green(` ✅ Found: ${priorityFile}`));
514
+ // Find the project type that matches this file
515
+ const matchingType = PROJECT_TYPES.find(type => type.files.includes(priorityFile));
516
+ if (matchingType) {
517
+ console.log(chalk.green(` 📦 Detected: ${matchingType.name}`));
518
+ return matchingType;
519
+ }
520
+ }
521
+ }
522
+ // Second pass: check all other files
503
523
  for (const projectType of PROJECT_TYPES) {
504
524
  console.log(chalk.gray(` Checking ${projectType.name}: ${projectType.files.join(', ')}`));
505
525
  for (const file of projectType.files) {
526
+ if (priorityFiles.includes(file))
527
+ continue; // Already checked
506
528
  // Check in current directory first
507
529
  const filePath = path.join(process.cwd(), file);
508
- console.log(chalk.gray(` Looking for: ${filePath}`));
509
530
  if (await fs.pathExists(filePath)) {
510
- console.log(chalk.green(` ✅ Found: ${file}`));
531
+ console.log(chalk.green(` ✅ Found: ${file}`));
511
532
  return projectType;
512
533
  }
513
- // Then check subdirectories for config files
514
- try {
515
- const currentDirContents = await fs.readdir(process.cwd());
516
- for (const item of currentDirContents) {
517
- const itemPath = path.join(process.cwd(), item);
518
- const stats = await fs.stat(itemPath);
519
- if (stats.isDirectory()) {
520
- const configPath = path.join(itemPath, file);
521
- if (await fs.pathExists(configPath)) {
522
- console.log(chalk.green(` ✅ Found in subdirectory: ${item}/${file}`));
523
- return projectType;
524
- }
534
+ }
535
+ }
536
+ // Third pass: check subdirectories
537
+ try {
538
+ const currentDirContents = await fs.readdir(process.cwd());
539
+ for (const item of currentDirContents) {
540
+ const itemPath = path.join(process.cwd(), item);
541
+ const stats = await fs.stat(itemPath);
542
+ if (stats.isDirectory()) {
543
+ for (const priorityFile of priorityFiles) {
544
+ const configPath = path.join(itemPath, priorityFile);
545
+ if (await fs.pathExists(configPath)) {
546
+ console.log(chalk.green(` ✅ Found in subdirectory: ${item}/${priorityFile}`));
547
+ const matchingType = PROJECT_TYPES.find(type => type.files.includes(priorityFile));
548
+ if (matchingType)
549
+ return matchingType;
525
550
  }
526
551
  }
527
552
  }
528
- catch (error) {
529
- // Ignore directory read errors
530
- }
531
553
  }
532
554
  }
555
+ catch (error) {
556
+ console.log(chalk.yellow(' Warning: Could not read directory contents'));
557
+ }
533
558
  console.log(chalk.yellow(' No project type detected'));
534
- return PROJECT_TYPES[0]; // Default to Node.js for single package checks
559
+ return null;
535
560
  }
536
561
  async function getDependenciesForProject(projectType) {
537
- // First check current directory
538
- for (const file of projectType.files) {
562
+ console.log(chalk.gray(`📋 Looking for dependencies in ${projectType.name} project...`));
563
+ // Priority order for dependency files
564
+ const priorityFiles = projectType.files.slice().sort((a, b) => {
565
+ const priority = ['package.json', 'Cargo.toml', 'requirements.txt', 'composer.json', 'go.mod'];
566
+ return priority.indexOf(a) - priority.indexOf(b);
567
+ });
568
+ // First check current directory with priority files
569
+ for (const file of priorityFiles) {
539
570
  const filePath = path.join(process.cwd(), file);
571
+ console.log(chalk.gray(` Checking: ${filePath}`));
540
572
  if (await fs.pathExists(filePath)) {
573
+ console.log(chalk.green(` ✅ Found: ${file}`));
541
574
  try {
542
575
  let content;
543
576
  if (file.endsWith('.json')) {
544
577
  content = await fs.readJson(filePath);
578
+ console.log(chalk.gray(` 📦 Loaded JSON config from ${file}`));
545
579
  }
546
580
  else if (file.endsWith('.toml')) {
547
581
  // Simple TOML parser for basic cases
548
582
  const tomlContent = await fs.readFile(filePath, 'utf-8');
549
583
  content = parseSimpleToml(tomlContent);
584
+ console.log(chalk.gray(` 📦 Loaded TOML config from ${file}`));
550
585
  }
551
586
  else {
552
587
  content = await fs.readFile(filePath, 'utf-8');
588
+ console.log(chalk.gray(` 📦 Loaded text config from ${file}`));
589
+ }
590
+ const dependencies = projectType.getDependencies(content, file);
591
+ const depCount = Object.keys(dependencies).length;
592
+ console.log(chalk.green(` 📊 Found ${depCount} dependencies`));
593
+ if (depCount > 0) {
594
+ console.log(chalk.gray(` Dependencies: ${Object.keys(dependencies).slice(0, 5).join(', ')}${depCount > 5 ? '...' : ''}`));
595
+ return dependencies;
553
596
  }
554
- return projectType.getDependencies(content, file);
555
597
  }
556
598
  catch (error) {
557
- console.warn(`⚠️ Could not parse ${file}`);
599
+ console.warn(chalk.yellow(` ⚠️ Could not parse ${file}: ${error}`));
558
600
  }
559
601
  }
560
602
  }
@@ -565,9 +607,10 @@ async function getDependenciesForProject(projectType) {
565
607
  const itemPath = path.join(process.cwd(), item);
566
608
  const stats = await fs.stat(itemPath);
567
609
  if (stats.isDirectory()) {
568
- for (const file of projectType.files) {
610
+ for (const file of priorityFiles) {
569
611
  const configPath = path.join(itemPath, file);
570
612
  if (await fs.pathExists(configPath)) {
613
+ console.log(chalk.green(` ✅ Found in subdirectory: ${item}/${file}`));
571
614
  try {
572
615
  let content;
573
616
  if (file.endsWith('.json')) {
@@ -580,10 +623,15 @@ async function getDependenciesForProject(projectType) {
580
623
  else {
581
624
  content = await fs.readFile(configPath, 'utf-8');
582
625
  }
583
- return projectType.getDependencies(content, file);
626
+ const dependencies = projectType.getDependencies(content, file);
627
+ const depCount = Object.keys(dependencies).length;
628
+ if (depCount > 0) {
629
+ console.log(chalk.green(` 📊 Found ${depCount} dependencies in ${configPath}`));
630
+ return dependencies;
631
+ }
584
632
  }
585
633
  catch (error) {
586
- console.warn(`⚠️ Could not parse ${configPath}`);
634
+ console.warn(chalk.yellow(` ⚠️ Could not parse ${configPath}`));
587
635
  }
588
636
  }
589
637
  }
@@ -591,8 +639,9 @@ async function getDependenciesForProject(projectType) {
591
639
  }
592
640
  }
593
641
  catch (error) {
594
- // Ignore directory read errors
642
+ console.warn(chalk.yellow(' ⚠️ Could not read directory contents'));
595
643
  }
644
+ console.log(chalk.yellow(' 📦 No dependencies found'));
596
645
  return {};
597
646
  }
598
647
  function parseSimpleToml(content) {