All files / src cli.ts

100% Statements 46/46
100% Branches 33/33
100% Functions 6/6
100% Lines 45/45

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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126                    2x   1x     1x   3x   2x       2x   2x                                         19x   19x 19x 19x   19x 29x 29x 3x 26x 2x 24x 3x 21x 3x 3x 2x     18x       19x                     19x   19x 2x 2x     17x 4x 4x     13x 13x 13x   10x 3x 3x     7x 4x   3x   7x   3x 3x 3x                            
import { readFileSync, writeFileSync } from 'node:fs';
import { layoutProcess } from './index';
 
export interface CliIo {
  stdout: (data: string) => void;
  stderr: (data: string) => void;
  readFile: (path: string, encoding: 'utf-8') => string;
  writeFile: (path: string, data: string, encoding: 'utf-8') => void;
}
 
const defaultIo: CliIo = {
  stdout: (data) => {
    process.stdout.write(data);
  },
  stderr: (data) => {
    process.stderr.write(data);
  },
  readFile: (path, encoding) => readFileSync(path, encoding),
  writeFile: (path, data, encoding) => {
    writeFileSync(path, data, encoding);
  },
};
 
export const VERSION = '0.1.0';
 
export const HELP_TEXT = `Usage: bpmn-auto-layout [options] [input] [output]
 
Auto-layout BPMN 2.0 diagrams, generating missing DI information.
 
Options:
  -i, --in-place         Overwrite the input file in place
  -s, --spacing <number> Set grid spacing between elements (default: 60)
  -v, --version          Show version number
  -h, --help             Show help
`;
 
interface ParsedArgs {
  inPlace: boolean;
  spacing?: number;
  showHelp: boolean;
  showVersion: boolean;
  input?: string;
  output?: string;
}
 
function parseCliArgs(args: string[]): ParsedArgs {
  let inPlace = false;
  let spacing: number | undefined;
  let showHelp = false;
  let showVersion = false;
  const positional: string[] = [];
 
  for (let i = 0; i < args.length; i++) {
    const arg = args[i];
    if (arg === '-h' || arg === '--help') {
      showHelp = true;
    } else if (arg === '-v' || arg === '--version') {
      showVersion = true;
    } else if (arg === '-i' || arg === '--in-place') {
      inPlace = true;
    } else if (arg === '-s' || arg === '--spacing') {
      i++;
      if (i < args.length) {
        spacing = Number(args[i]);
      }
    } else {
      positional.push(arg);
    }
  }
 
  return {
    inPlace,
    spacing,
    showHelp,
    showVersion,
    input: positional[0],
    output: positional[1],
  };
}
 
export async function runCli(args: string[], io: CliIo = defaultIo): Promise<number> {
  const parsed = parseCliArgs(args);
 
  if (parsed.showVersion) {
    io.stdout(`${VERSION}\n`);
    return 0;
  }
 
  if (parsed.showHelp || !parsed.input) {
    io.stdout(HELP_TEXT);
    return 0;
  }
 
  try {
    const xml = io.readFile(parsed.input, 'utf-8');
    const result = await layoutProcess(xml, { gridSpacing: parsed.spacing });
 
    if (parsed.inPlace) {
      io.writeFile(parsed.input, result, 'utf-8');
      return 0;
    }
 
    if (parsed.output && parsed.output !== '-') {
      io.writeFile(parsed.output, result, 'utf-8');
    } else {
      io.stdout(result);
    }
    return 0;
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    io.stderr(`Error: ${message}\n`);
    return 1;
  }
}
 
/* v8 ignore start */
const isMain =
  process.argv[1] && (process.argv[1].endsWith('cli.js') || process.argv[1].endsWith('cli.ts'));
 
if (isMain) {
  runCli(process.argv.slice(2)).then((code) => {
    process.exitCode = code;
  });
}
/* v8 ignore stop */