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               38x 38x   38x 43x 42x   41x 41x   40x 27x 1x   26x 26x           13x 10x             10x   3x               3x     22x 13x 1x     12x 20x 20x       33x       3x 3x 3x   3x 12x 9x     3x       41x 41x   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;
  }
}