time-to-botec

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

parse.js (3065B)


      1 'use strict';
      2 
      3 const path = require('path');
      4 const resolveCommand = require('./util/resolveCommand');
      5 const escape = require('./util/escape');
      6 const readShebang = require('./util/readShebang');
      7 
      8 const isWin = process.platform === 'win32';
      9 const isExecutableRegExp = /\.(?:com|exe)$/i;
     10 const isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;
     11 
     12 function detectShebang(parsed) {
     13     parsed.file = resolveCommand(parsed);
     14 
     15     const shebang = parsed.file && readShebang(parsed.file);
     16 
     17     if (shebang) {
     18         parsed.args.unshift(parsed.file);
     19         parsed.command = shebang;
     20 
     21         return resolveCommand(parsed);
     22     }
     23 
     24     return parsed.file;
     25 }
     26 
     27 function parseNonShell(parsed) {
     28     if (!isWin) {
     29         return parsed;
     30     }
     31 
     32     // Detect & add support for shebangs
     33     const commandFile = detectShebang(parsed);
     34 
     35     // We don't need a shell if the command filename is an executable
     36     const needsShell = !isExecutableRegExp.test(commandFile);
     37 
     38     // If a shell is required, use cmd.exe and take care of escaping everything correctly
     39     // Note that `forceShell` is an hidden option used only in tests
     40     if (parsed.options.forceShell || needsShell) {
     41         // Need to double escape meta chars if the command is a cmd-shim located in `node_modules/.bin/`
     42         // The cmd-shim simply calls execute the package bin file with NodeJS, proxying any argument
     43         // Because the escape of metachars with ^ gets interpreted when the cmd.exe is first called,
     44         // we need to double escape them
     45         const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
     46 
     47         // Normalize posix paths into OS compatible paths (e.g.: foo/bar -> foo\bar)
     48         // This is necessary otherwise it will always fail with ENOENT in those cases
     49         parsed.command = path.normalize(parsed.command);
     50 
     51         // Escape command & arguments
     52         parsed.command = escape.command(parsed.command);
     53         parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars));
     54 
     55         const shellCommand = [parsed.command].concat(parsed.args).join(' ');
     56 
     57         parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`];
     58         parsed.command = process.env.comspec || 'cmd.exe';
     59         parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped
     60     }
     61 
     62     return parsed;
     63 }
     64 
     65 function parse(command, args, options) {
     66     // Normalize arguments, similar to nodejs
     67     if (args && !Array.isArray(args)) {
     68         options = args;
     69         args = null;
     70     }
     71 
     72     args = args ? args.slice(0) : []; // Clone array to avoid changing the original
     73     options = Object.assign({}, options); // Clone object to avoid changing the original
     74 
     75     // Build our parsed object
     76     const parsed = {
     77         command,
     78         args,
     79         options,
     80         file: undefined,
     81         original: {
     82             command,
     83             args,
     84         },
     85     };
     86 
     87     // Delegate further parsing to shell or non-shell
     88     return options.shell ? parsed : parseNonShell(parsed);
     89 }
     90 
     91 module.exports = parse;