All files inputs.ts

100% Statements 43/43
100% Branches 20/20
100% Functions 5/5
100% Lines 36/36

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90        2x               43x 43x   43x 48x 47x   46x 46x   45x 28x 1x   27x 27x           17x 14x             14x   3x               3x     27x 17x 1x     16x 25x 25x       38x       3x 3x 3x   3x 12x 9x     3x       46x 46x   4x 1x      
import { stat } from "node:fs/promises";
import path from "node:path";
import { glob, isDynamicPattern } from "tinyglobby";
 
const javaScriptFilePattern = /\.(?:cjs|js|mjs)$/i;
 
export interface ResolvedInput {
  path: string;
  root: string;
}
 
export async function resolveInputs(patterns: readonly string[], cwd: string): Promise<ResolvedInput[]> {
  const inputs = patterns.length ? patterns : ["dist"];
  const resolved = new Map<string, ResolvedInput>();
 
  for (const input of inputs) {
    if (!input) throw new Error("Input paths and patterns cannot be empty");
    if (input.startsWith("!")) throw new Error(`Negated input patterns are not supported: ${input}`);
 
    const literalPath = path.resolve(cwd, input);
    const fileStat = await tryStat(literalPath);
 
    if (fileStat?.isFile()) {
      if (!javaScriptFilePattern.test(literalPath)) {
        throw new Error(`Unsupported input file extension: ${input}`);
      }
      resolved.set(literalPath, { path: literalPath, root: cwd });
      continue;
    }
 
    let matches: string[];
    let root: string;
 
    if (fileStat?.isDirectory()) {
      matches = await glob("**/*", {
        absolute: true,
        cwd: literalPath,
        dot: true,
        followSymbolicLinks: false,
        onlyFiles: true,
      });
      root = literalPath;
    } else {
      matches = await glob(input, {
        absolute: true,
        cwd,
        dot: true,
        expandDirectories: true,
        followSymbolicLinks: false,
        onlyFiles: true,
      });
      root = findGlobRoot(input, cwd);
    }
 
    const javaScriptMatches = matches.filter((file) => javaScriptFilePattern.test(file));
    if (!javaScriptMatches.length) {
      throw new Error(`Input did not match any JavaScript files: ${input}`);
    }
 
    for (const file of javaScriptMatches) {
      const absolutePath = path.resolve(file);
      if (!resolved.has(absolutePath)) resolved.set(absolutePath, { path: absolutePath, root });
    }
  }
 
  return [...resolved.values()].sort((left, right) => left.path.localeCompare(right.path));
}
 
function findGlobRoot(pattern: string, cwd: string): string {
  const absolutePattern = path.resolve(cwd, pattern);
  const parsed = path.parse(absolutePattern);
  let root = parsed.root;
 
  for (const segment of absolutePattern.slice(parsed.root.length).split(path.sep)) {
    if (isDynamicPattern(segment, { caseSensitiveMatch: process.platform !== "win32" })) break;
    root = path.join(root, segment);
  }
 
  return root;
}
 
async function tryStat(filePath: string) {
  try {
    return await stat(filePath);
  } catch (error) {
    if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
    throw error;
  }
}