package-installer-cli 1.2.0 → 1.3.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.
@@ -3,11 +3,11 @@ 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
11
  import { getSupportedLanguages, getLanguageConfig } from '../utils/languageConfig.js';
12
12
  const execAsync = promisify(exec);
13
13
  // Generate PROJECT_TYPES from shared language configuration with enhanced registry support
@@ -18,7 +18,6 @@ const PROJECT_TYPES = getSupportedLanguages().map(lang => {
18
18
  let registryUrl;
19
19
  let packageInfoUrl;
20
20
  switch (lang) {
21
- case 'nodejs':
22
21
  case 'javascript':
23
22
  case 'typescript':
24
23
  registryUrl = 'https://registry.npmjs.org';
@@ -48,7 +47,8 @@ const PROJECT_TYPES = getSupportedLanguages().map(lang => {
48
47
  const deps = {};
49
48
  // Language-specific dependency parsing
50
49
  switch (lang) {
51
- case 'nodejs':
50
+ case 'javascript':
51
+ case 'typescript':
52
52
  if (filename === 'package.json' || filename === 'package-lock.json' || filename === 'pnpm-lock.yml') {
53
53
  return {
54
54
  ...content.dependencies,
@@ -104,14 +104,6 @@ const PROJECT_TYPES = getSupportedLanguages().map(lang => {
104
104
  }
105
105
  }
106
106
  break;
107
- case 'php':
108
- if (filename === 'composer.json') {
109
- return {
110
- ...content.require,
111
- ...content['require-dev']
112
- };
113
- }
114
- break;
115
107
  case 'ruby':
116
108
  if (filename === 'Gemfile') {
117
109
  const lines = content.toString().split('\n');
@@ -159,16 +151,13 @@ const PROJECT_TYPES = getSupportedLanguages().map(lang => {
159
151
  });
160
152
  function getRegistryUrl(lang) {
161
153
  switch (lang) {
162
- case 'nodejs': return 'https://registry.npmjs.org';
154
+ case 'javascript':
155
+ case 'typescript':
156
+ return 'https://registry.npmjs.org';
163
157
  case 'rust': return 'https://crates.io/api/v1/crates';
164
158
  case 'python': return 'https://pypi.org/pypi';
165
159
  case 'go': return 'https://proxy.golang.org';
166
160
  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
161
  default: return '';
173
162
  }
174
163
  }
@@ -191,7 +180,8 @@ async function fetchPackageFromRegistry(packageName, projectType) {
191
180
  const parsed = JSON.parse(data);
192
181
  // Handle different registry response formats
193
182
  switch (projectType.name) {
194
- case 'Node.js':
183
+ case 'JavaScript':
184
+ case 'Typescript':
195
185
  resolve(parseNpmRegistryResponse(parsed, packageName));
196
186
  break;
197
187
  case 'Rust':
@@ -293,7 +283,8 @@ function parseGenericResponse(data, packageName) {
293
283
  async function getLatestVersion(packageName, projectType) {
294
284
  try {
295
285
  switch (projectType.name) {
296
- case 'Node.js':
286
+ case 'JavaScript':
287
+ case 'TypeScript':
297
288
  const { stdout } = await execAsync(`npm view ${packageName} version`);
298
289
  return stdout.trim();
299
290
  case 'Rust':
@@ -380,33 +371,43 @@ async function getEnhancedPackageInfo(name, currentVersion, projectType) {
380
371
  * Display help for check command
381
372
  */
382
373
  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
- }));
374
+ const helpConfig = {
375
+ commandName: 'Check',
376
+ emoji: '🔍',
377
+ description: 'Check package versions in your project and get suggestions for updates.\nHelps you keep your dependencies up-to-date and secure.',
378
+ usage: [
379
+ 'check [package-name] [options]',
380
+ 'check [options]'
381
+ ],
382
+ options: [
383
+ { flag: '-h, --help', description: 'Display help for this command' },
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
@@ -500,61 +501,100 @@ async function checkProjectPackages(verbose = false) {
500
501
  }
501
502
  async function detectProjectType() {
502
503
  console.log(chalk.gray('🔍 Detecting project type...'));
504
+ // Priority order for detection - check most common files first
505
+ const priorityFiles = ['package.json', 'Cargo.toml', 'requirements.txt', 'composer.json', 'go.mod'];
506
+ // First pass: check priority files in current directory
507
+ for (const priorityFile of priorityFiles) {
508
+ const filePath = path.join(process.cwd(), priorityFile);
509
+ console.log(chalk.gray(` Checking priority file: ${filePath}`));
510
+ if (await fs.pathExists(filePath)) {
511
+ console.log(chalk.green(` ✅ Found: ${priorityFile}`));
512
+ // Find the project type that matches this file
513
+ const matchingType = PROJECT_TYPES.find(type => type.files.includes(priorityFile));
514
+ if (matchingType) {
515
+ console.log(chalk.green(` 📦 Detected: ${matchingType.name}`));
516
+ return matchingType;
517
+ }
518
+ }
519
+ }
520
+ // Second pass: check all other files
503
521
  for (const projectType of PROJECT_TYPES) {
504
522
  console.log(chalk.gray(` Checking ${projectType.name}: ${projectType.files.join(', ')}`));
505
523
  for (const file of projectType.files) {
524
+ if (priorityFiles.includes(file))
525
+ continue; // Already checked
506
526
  // Check in current directory first
507
527
  const filePath = path.join(process.cwd(), file);
508
- console.log(chalk.gray(` Looking for: ${filePath}`));
509
528
  if (await fs.pathExists(filePath)) {
510
- console.log(chalk.green(` ✅ Found: ${file}`));
529
+ console.log(chalk.green(` ✅ Found: ${file}`));
511
530
  return projectType;
512
531
  }
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
- }
532
+ }
533
+ }
534
+ // Third pass: check subdirectories
535
+ try {
536
+ const currentDirContents = await fs.readdir(process.cwd());
537
+ for (const item of currentDirContents) {
538
+ const itemPath = path.join(process.cwd(), item);
539
+ const stats = await fs.stat(itemPath);
540
+ if (stats.isDirectory()) {
541
+ for (const priorityFile of priorityFiles) {
542
+ const configPath = path.join(itemPath, priorityFile);
543
+ if (await fs.pathExists(configPath)) {
544
+ console.log(chalk.green(` ✅ Found in subdirectory: ${item}/${priorityFile}`));
545
+ const matchingType = PROJECT_TYPES.find(type => type.files.includes(priorityFile));
546
+ if (matchingType)
547
+ return matchingType;
525
548
  }
526
549
  }
527
550
  }
528
- catch (error) {
529
- // Ignore directory read errors
530
- }
531
551
  }
532
552
  }
553
+ catch (error) {
554
+ console.log(chalk.yellow(' Warning: Could not read directory contents'));
555
+ }
533
556
  console.log(chalk.yellow(' No project type detected'));
534
- return PROJECT_TYPES[0]; // Default to Node.js for single package checks
557
+ return null;
535
558
  }
536
559
  async function getDependenciesForProject(projectType) {
537
- // First check current directory
538
- for (const file of projectType.files) {
560
+ console.log(chalk.gray(`📋 Looking for dependencies in ${projectType.name} project...`));
561
+ // Priority order for dependency files
562
+ const priorityFiles = projectType.files.slice().sort((a, b) => {
563
+ const priority = ['package.json', 'Cargo.toml', 'requirements.txt', 'composer.json', 'go.mod'];
564
+ return priority.indexOf(a) - priority.indexOf(b);
565
+ });
566
+ // First check current directory with priority files
567
+ for (const file of priorityFiles) {
539
568
  const filePath = path.join(process.cwd(), file);
569
+ console.log(chalk.gray(` Checking: ${filePath}`));
540
570
  if (await fs.pathExists(filePath)) {
571
+ console.log(chalk.green(` ✅ Found: ${file}`));
541
572
  try {
542
573
  let content;
543
574
  if (file.endsWith('.json')) {
544
575
  content = await fs.readJson(filePath);
576
+ console.log(chalk.gray(` 📦 Loaded JSON config from ${file}`));
545
577
  }
546
578
  else if (file.endsWith('.toml')) {
547
579
  // Simple TOML parser for basic cases
548
580
  const tomlContent = await fs.readFile(filePath, 'utf-8');
549
581
  content = parseSimpleToml(tomlContent);
582
+ console.log(chalk.gray(` 📦 Loaded TOML config from ${file}`));
550
583
  }
551
584
  else {
552
585
  content = await fs.readFile(filePath, 'utf-8');
586
+ console.log(chalk.gray(` 📦 Loaded text config from ${file}`));
587
+ }
588
+ const dependencies = projectType.getDependencies(content, file);
589
+ const depCount = Object.keys(dependencies).length;
590
+ console.log(chalk.green(` 📊 Found ${depCount} dependencies`));
591
+ if (depCount > 0) {
592
+ console.log(chalk.gray(` Dependencies: ${Object.keys(dependencies).slice(0, 5).join(', ')}${depCount > 5 ? '...' : ''}`));
593
+ return dependencies;
553
594
  }
554
- return projectType.getDependencies(content, file);
555
595
  }
556
596
  catch (error) {
557
- console.warn(`⚠️ Could not parse ${file}`);
597
+ console.warn(chalk.yellow(` ⚠️ Could not parse ${file}: ${error}`));
558
598
  }
559
599
  }
560
600
  }
@@ -565,9 +605,10 @@ async function getDependenciesForProject(projectType) {
565
605
  const itemPath = path.join(process.cwd(), item);
566
606
  const stats = await fs.stat(itemPath);
567
607
  if (stats.isDirectory()) {
568
- for (const file of projectType.files) {
608
+ for (const file of priorityFiles) {
569
609
  const configPath = path.join(itemPath, file);
570
610
  if (await fs.pathExists(configPath)) {
611
+ console.log(chalk.green(` ✅ Found in subdirectory: ${item}/${file}`));
571
612
  try {
572
613
  let content;
573
614
  if (file.endsWith('.json')) {
@@ -580,10 +621,15 @@ async function getDependenciesForProject(projectType) {
580
621
  else {
581
622
  content = await fs.readFile(configPath, 'utf-8');
582
623
  }
583
- return projectType.getDependencies(content, file);
624
+ const dependencies = projectType.getDependencies(content, file);
625
+ const depCount = Object.keys(dependencies).length;
626
+ if (depCount > 0) {
627
+ console.log(chalk.green(` 📊 Found ${depCount} dependencies in ${configPath}`));
628
+ return dependencies;
629
+ }
584
630
  }
585
631
  catch (error) {
586
- console.warn(`⚠️ Could not parse ${configPath}`);
632
+ console.warn(chalk.yellow(` ⚠️ Could not parse ${configPath}`));
587
633
  }
588
634
  }
589
635
  }
@@ -591,8 +637,9 @@ async function getDependenciesForProject(projectType) {
591
637
  }
592
638
  }
593
639
  catch (error) {
594
- // Ignore directory read errors
640
+ console.warn(chalk.yellow(' ⚠️ Could not read directory contents'));
595
641
  }
642
+ console.log(chalk.yellow(' 📦 No dependencies found'));
596
643
  return {};
597
644
  }
598
645
  function parseSimpleToml(content) {