time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

command.js (3071B)


      1 import {Buffer} from 'node:buffer';
      2 import {ChildProcess} from 'node:child_process';
      3 
      4 const normalizeArgs = (file, args = []) => {
      5 	if (!Array.isArray(args)) {
      6 		return [file];
      7 	}
      8 
      9 	return [file, ...args];
     10 };
     11 
     12 const NO_ESCAPE_REGEXP = /^[\w.-]+$/;
     13 const DOUBLE_QUOTES_REGEXP = /"/g;
     14 
     15 const escapeArg = arg => {
     16 	if (typeof arg !== 'string' || NO_ESCAPE_REGEXP.test(arg)) {
     17 		return arg;
     18 	}
     19 
     20 	return `"${arg.replace(DOUBLE_QUOTES_REGEXP, '\\"')}"`;
     21 };
     22 
     23 export const joinCommand = (file, args) => normalizeArgs(file, args).join(' ');
     24 
     25 export const getEscapedCommand = (file, args) => normalizeArgs(file, args).map(arg => escapeArg(arg)).join(' ');
     26 
     27 const SPACES_REGEXP = / +/g;
     28 
     29 // Handle `execaCommand()`
     30 export const parseCommand = command => {
     31 	const tokens = [];
     32 	for (const token of command.trim().split(SPACES_REGEXP)) {
     33 		// Allow spaces to be escaped by a backslash if not meant as a delimiter
     34 		const previousToken = tokens[tokens.length - 1];
     35 		if (previousToken && previousToken.endsWith('\\')) {
     36 			// Merge previous token with current one
     37 			tokens[tokens.length - 1] = `${previousToken.slice(0, -1)} ${token}`;
     38 		} else {
     39 			tokens.push(token);
     40 		}
     41 	}
     42 
     43 	return tokens;
     44 };
     45 
     46 const parseExpression = expression => {
     47 	const typeOfExpression = typeof expression;
     48 
     49 	if (typeOfExpression === 'string') {
     50 		return expression;
     51 	}
     52 
     53 	if (typeOfExpression === 'number') {
     54 		return String(expression);
     55 	}
     56 
     57 	if (
     58 		typeOfExpression === 'object'
     59 		&& expression !== null
     60 		&& !(expression instanceof ChildProcess)
     61 		&& 'stdout' in expression
     62 	) {
     63 		const typeOfStdout = typeof expression.stdout;
     64 
     65 		if (typeOfStdout === 'string') {
     66 			return expression.stdout;
     67 		}
     68 
     69 		if (Buffer.isBuffer(expression.stdout)) {
     70 			return expression.stdout.toString();
     71 		}
     72 
     73 		throw new TypeError(`Unexpected "${typeOfStdout}" stdout in template expression`);
     74 	}
     75 
     76 	throw new TypeError(`Unexpected "${typeOfExpression}" in template expression`);
     77 };
     78 
     79 const concatTokens = (tokens, nextTokens, isNew) => isNew || tokens.length === 0 || nextTokens.length === 0
     80 	? [...tokens, ...nextTokens]
     81 	: [
     82 		...tokens.slice(0, -1),
     83 		`${tokens[tokens.length - 1]}${nextTokens[0]}`,
     84 		...nextTokens.slice(1),
     85 	];
     86 
     87 const parseTemplate = ({templates, expressions, tokens, index, template}) => {
     88 	const templateString = template ?? templates.raw[index];
     89 	const templateTokens = templateString.split(SPACES_REGEXP).filter(Boolean);
     90 	const newTokens = concatTokens(
     91 		tokens,
     92 		templateTokens,
     93 		templateString.startsWith(' '),
     94 	);
     95 
     96 	if (index === expressions.length) {
     97 		return newTokens;
     98 	}
     99 
    100 	const expression = expressions[index];
    101 	const expressionTokens = Array.isArray(expression)
    102 		? expression.map(expression => parseExpression(expression))
    103 		: [parseExpression(expression)];
    104 	return concatTokens(
    105 		newTokens,
    106 		expressionTokens,
    107 		templateString.endsWith(' '),
    108 	);
    109 };
    110 
    111 export const parseTemplates = (templates, expressions) => {
    112 	let tokens = [];
    113 
    114 	for (const [index, template] of templates.entries()) {
    115 		tokens = parseTemplate({templates, expressions, tokens, index, template});
    116 	}
    117 
    118 	return tokens;
    119 };
    120