@safetnsr/vet 1.17.0 → 1.17.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.
@@ -426,14 +426,18 @@ function findOrphanedExports(cwd, files) {
426
426
  // Also scan for dynamic imports: require('x'), import('x') — to catch non-static usage
427
427
  const dynamicImportRe = /(?:require|import)\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
428
428
  const allSourceFiles = files.filter(f => isSourceFile(f));
429
+ // Track which files are re-exported via `export * from './x'`
430
+ const reExportedFiles = new Set();
431
+ const exportFromRe = /export\s+(?:type\s+)?\*\s+from\s+['"]([^'"]+)['"]/g;
432
+ const exportNamedFromRe = /export\s+(?:type\s+)?\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/g;
429
433
  for (const file of allSourceFiles) {
430
434
  const content = readFile(join(cwd, file));
431
435
  if (!content)
432
436
  continue;
433
437
  let match;
438
+ // Standard imports
434
439
  importRe.lastIndex = 0;
435
440
  while ((match = importRe.exec(content)) !== null) {
436
- // Named imports: { a, b as c }
437
441
  const namedParts = [match[1], match[3]].filter(Boolean);
438
442
  for (const part of namedParts) {
439
443
  for (const name of part.split(',')) {
@@ -442,10 +446,38 @@ function findOrphanedExports(cwd, files) {
442
446
  importedNames.add(trimmed);
443
447
  }
444
448
  }
445
- // Default import
446
449
  if (match[2])
447
450
  importedNames.add(match[2]);
448
451
  }
452
+ // `export * from './module'` — all exports from that module are consumed
453
+ exportFromRe.lastIndex = 0;
454
+ while ((match = exportFromRe.exec(content)) !== null) {
455
+ // Resolve the re-exported file relative to current file
456
+ const specifier = match[1];
457
+ if (specifier.startsWith('.')) {
458
+ const dir = dirname(file);
459
+ const candidates = [
460
+ join(dir, specifier),
461
+ join(dir, specifier + '.ts'), join(dir, specifier + '.tsx'),
462
+ join(dir, specifier + '.js'), join(dir, specifier + '.jsx'),
463
+ join(dir, specifier, 'index.ts'), join(dir, specifier, 'index.tsx'),
464
+ join(dir, specifier, 'index.js'),
465
+ ];
466
+ for (const c of candidates) {
467
+ const normalized = c.replace(/\\/g, '/');
468
+ reExportedFiles.add(normalized);
469
+ }
470
+ }
471
+ }
472
+ // `export { name } from './module'` — named re-exports count as imports
473
+ exportNamedFromRe.lastIndex = 0;
474
+ while ((match = exportNamedFromRe.exec(content)) !== null) {
475
+ for (const name of match[1].split(',')) {
476
+ const trimmed = name.trim().split(/\s+as\s+/)[0].trim();
477
+ if (trimmed)
478
+ importedNames.add(trimmed);
479
+ }
480
+ }
449
481
  }
450
482
  // Build a cross-reference map: for each exported name, check if it appears in other files
451
483
  // This catches hook returns ({ Component } = useHook()), dynamic usage, re-exports, JSX, etc.
@@ -468,14 +500,17 @@ function findOrphanedExports(cwd, files) {
468
500
  const mono = isMonorepo(cwd);
469
501
  for (const exp of exports) {
470
502
  if (!importedNames.has(exp.name)) {
503
+ // Skip if the file is re-exported via `export * from './file'`
504
+ const normalizedFile = exp.file.replace(/\\/g, '/');
505
+ if (reExportedFiles.has(normalizedFile))
506
+ continue;
471
507
  // Cross-reference check: if the export name appears in a different file, it's likely used
472
- // (catches hook returns, JSX usage, dynamic imports, re-exports)
473
508
  const refs = nameToFiles.get(exp.name);
474
509
  if (refs) {
475
510
  const otherFiles = new Set(refs);
476
511
  otherFiles.delete(exp.file);
477
512
  if (otherFiles.size > 0)
478
- continue; // referenced in another file → not orphaned
513
+ continue;
479
514
  }
480
515
  // Skip framework convention exports (Next.js, Remix, SvelteKit, Nuxt)
481
516
  if (FRAMEWORK_CONVENTION_EXPORTS.has(exp.name) && isFrameworkConventionFile(exp.file))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@safetnsr/vet",
3
- "version": "1.17.0",
3
+ "version": "1.17.1",
4
4
  "description": "vet your AI-generated code — one command, one score card, one letter grade",
5
5
  "type": "module",
6
6
  "bin": {