82 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			82 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
/**
 | 
						|
 * Return only the user arguments (excluding runtime executable and script path),
 | 
						|
 * across Node.js, Deno (run/compiled), and Bun.
 | 
						|
 *
 | 
						|
 * - Deno: uses Deno.args directly (already user-only in both run and compile).
 | 
						|
 * - Node/Bun: uses process.execPath's basename to decide if there is a script arg.
 | 
						|
 *   If execPath basename is a known launcher (node/nodejs/bun/deno), skip 2; else skip 1.
 | 
						|
 */
 | 
						|
export function getUserArgs(argv?: string[]): string[] {
 | 
						|
  // If argv is explicitly provided, use it instead of Deno.args
 | 
						|
  // This handles test scenarios where process.argv is manually modified
 | 
						|
  const useProvidedArgv = argv !== undefined;
 | 
						|
 | 
						|
  // Prefer Deno.args when available and no custom argv provided;
 | 
						|
  // it's the most reliable for Deno run and compiled.
 | 
						|
  // Deno.args is ALWAYS correct in Deno environments - it handles the internal bundle path automatically.
 | 
						|
  // deno-lint-ignore no-explicit-any
 | 
						|
  const g: any = typeof globalThis !== 'undefined' ? globalThis : {};
 | 
						|
 | 
						|
  if (!useProvidedArgv && g.Deno && g.Deno.args && Array.isArray(g.Deno.args)) {
 | 
						|
    return g.Deno.args.slice();
 | 
						|
  }
 | 
						|
 | 
						|
  const a = argv ?? (typeof process !== 'undefined' && Array.isArray(process.argv) ? process.argv : []);
 | 
						|
 | 
						|
  if (!Array.isArray(a) || a.length === 0) return [];
 | 
						|
 | 
						|
  // Determine execPath in Node/Bun (or compat shims)
 | 
						|
  let execPath = '';
 | 
						|
  if (typeof process !== 'undefined' && typeof process.execPath === 'string') {
 | 
						|
    execPath = process.execPath;
 | 
						|
  } else if (g.Deno && typeof g.Deno.execPath === 'function') {
 | 
						|
    // Fallback for unusual shims: try Deno.execPath() if present.
 | 
						|
    try {
 | 
						|
      execPath = g.Deno.execPath();
 | 
						|
    } catch {
 | 
						|
      /* ignore */
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  const base = basename(execPath).toLowerCase();
 | 
						|
  const knownLaunchers = new Set([
 | 
						|
    'node',
 | 
						|
    'node.exe',
 | 
						|
    'nodejs',
 | 
						|
    'nodejs.exe',
 | 
						|
    'bun',
 | 
						|
    'bun.exe',
 | 
						|
    'deno',
 | 
						|
    'deno.exe',
 | 
						|
    'tsx',
 | 
						|
    'tsx.exe',
 | 
						|
    'ts-node',
 | 
						|
    'ts-node.exe',
 | 
						|
  ]);
 | 
						|
 | 
						|
  // Always skip the executable (argv[0]).
 | 
						|
  let offset = Math.min(1, a.length);
 | 
						|
 | 
						|
  // If the executable is a known runtime launcher, there's almost always a script path in argv[1].
 | 
						|
  // This handles Node, Bun, and "deno run" (but NOT "deno compile" which won't match 'deno').
 | 
						|
  if (knownLaunchers.has(base)) {
 | 
						|
    offset = Math.min(2, a.length);
 | 
						|
  }
 | 
						|
 | 
						|
  // Safety: if offset would skip all elements and array is not empty, don't skip anything
 | 
						|
  // This handles edge cases like test environments with unusual argv setups
 | 
						|
  if (offset >= a.length && a.length > 0) {
 | 
						|
    offset = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  // Note: we intentionally avoid path/URL heuristics on argv[1] so we don't
 | 
						|
  // accidentally drop the first user arg when it's a path-like value in compiled mode.
 | 
						|
  return a.slice(offset);
 | 
						|
}
 | 
						|
 | 
						|
function basename(p: string): string {
 | 
						|
  if (!p) return '';
 | 
						|
  const parts = p.split(/[/\\]/);
 | 
						|
  return parts[parts.length - 1] || '';
 | 
						|
}
 |