|
- import {Buffer} from 'node:buffer';
- import {ChildProcess} from 'node:child_process';
-
- const normalizeArgs = (file, args = []) => {
- if (!Array.isArray(args)) {
- return [file];
- }
-
- return [file, ...args];
- };
-
- const NO_ESCAPE_REGEXP = /^[\w.-]+$/;
- const DOUBLE_QUOTES_REGEXP = /"/g;
-
- const escapeArg = arg => {
- if (typeof arg !== 'string' || NO_ESCAPE_REGEXP.test(arg)) {
- return arg;
- }
-
- return `"${arg.replace(DOUBLE_QUOTES_REGEXP, '\\"')}"`;
- };
-
- export const joinCommand = (file, args) => normalizeArgs(file, args).join(' ');
-
- export const getEscapedCommand = (file, args) => normalizeArgs(file, args).map(arg => escapeArg(arg)).join(' ');
-
- const SPACES_REGEXP = / +/g;
-
- // Handle `execaCommand()`
- export const parseCommand = command => {
- const tokens = [];
- for (const token of command.trim().split(SPACES_REGEXP)) {
- // Allow spaces to be escaped by a backslash if not meant as a delimiter
- const previousToken = tokens[tokens.length - 1];
- if (previousToken && previousToken.endsWith('\\')) {
- // Merge previous token with current one
- tokens[tokens.length - 1] = `${previousToken.slice(0, -1)} ${token}`;
- } else {
- tokens.push(token);
- }
- }
-
- return tokens;
- };
-
- const parseExpression = expression => {
- const typeOfExpression = typeof expression;
-
- if (typeOfExpression === 'string') {
- return expression;
- }
-
- if (typeOfExpression === 'number') {
- return String(expression);
- }
-
- if (
- typeOfExpression === 'object'
- && expression !== null
- && !(expression instanceof ChildProcess)
- && 'stdout' in expression
- ) {
- const typeOfStdout = typeof expression.stdout;
-
- if (typeOfStdout === 'string') {
- return expression.stdout;
- }
-
- if (Buffer.isBuffer(expression.stdout)) {
- return expression.stdout.toString();
- }
-
- throw new TypeError(`Unexpected "${typeOfStdout}" stdout in template expression`);
- }
-
- throw new TypeError(`Unexpected "${typeOfExpression}" in template expression`);
- };
-
- const concatTokens = (tokens, nextTokens, isNew) => isNew || tokens.length === 0 || nextTokens.length === 0
- ? [...tokens, ...nextTokens]
- : [
- ...tokens.slice(0, -1),
- `${tokens[tokens.length - 1]}${nextTokens[0]}`,
- ...nextTokens.slice(1),
- ];
-
- const parseTemplate = ({templates, expressions, tokens, index, template}) => {
- const templateString = template ?? templates.raw[index];
- const templateTokens = templateString.split(SPACES_REGEXP).filter(Boolean);
- const newTokens = concatTokens(
- tokens,
- templateTokens,
- templateString.startsWith(' '),
- );
-
- if (index === expressions.length) {
- return newTokens;
- }
-
- const expression = expressions[index];
- const expressionTokens = Array.isArray(expression)
- ? expression.map(expression => parseExpression(expression))
- : [parseExpression(expression)];
- return concatTokens(
- newTokens,
- expressionTokens,
- templateString.endsWith(' '),
- );
- };
-
- export const parseTemplates = (templates, expressions) => {
- let tokens = [];
-
- for (const [index, template] of templates.entries()) {
- tokens = parseTemplate({templates, expressions, tokens, index, template});
- }
-
- return tokens;
- };
|