diff --git a/lib/utils/escape-exec-path.js b/lib/utils/escape-exec-path.js index 42b64934867dd..e941e0cb9d5f7 100644 --- a/lib/utils/escape-exec-path.js +++ b/lib/utils/escape-exec-path.js @@ -1,6 +1,5 @@ -'use strict' -var path = require('path') -var isWindows = require('./is-windows.js') +const { normalize } = require('path') +const isWindows = require('./is-windows.js') /* Escape the name of an executable suitable for passing to the system shell. @@ -9,22 +8,14 @@ Windows is easy, wrap in double quotes and you're done, as there's no facility to create files with quotes in their names. Unix-likes are a little more complicated, wrap in single quotes and escape -any single quotes in the filename. +any single quotes in the filename. The '"'"' construction ends the quoted +block, creates a new " quoted string with ' in it. So, `foo'bar` becomes +`'foo'"'"'bar'`, which is the bash way of saying `'foo' + "'" + 'bar'`. */ -module.exports = escapify +const winQuote = str => !/ /.test(str) ? str : '"' + str + '"' +const winEsc = str => normalize(str).split(/\\/).map(winQuote).join('\\') -function windowsQuotes (str) { - if (!/ /.test(str)) return str - return '"' + str + '"' -} - -function escapify (str) { - if (isWindows) { - return path.normalize(str).split(/\\/).map(windowsQuotes).join('\\') - } else if (/[^-_.~/\w]/.test(str)) { - return "'" + str.replace(/'/g, "'\"'\"'") + "'" - } else { - return str - } -} +module.exports = str => isWindows ? winEsc(str) + : /[^-_.~/\w]/.test(str) ? "'" + str.replace(/'/g, `'"'"'`) + "'" + : str diff --git a/test/lib/utils/escape-exec-path.js b/test/lib/utils/escape-exec-path.js new file mode 100644 index 0000000000000..28fe75c2a98f4 --- /dev/null +++ b/test/lib/utils/escape-exec-path.js @@ -0,0 +1,15 @@ +const requireInject = require('require-inject') +const t = require('tap') +const getEscape = win => requireInject('../../../lib/utils/escape-exec-path.js', { + '../../../lib/utils/is-windows.js': win, + path: require('path')[win ? 'win32' : 'posix'] +}) + +const winEscape = getEscape(true) +const nixEscape = getEscape(false) + +t.equal(winEscape('hello/to the/world'), 'hello\\"to the"\\world') +t.equal(nixEscape(`hello/to-the/world`), `hello/to-the/world`) +t.equal(nixEscape(`hello/to the/world`), `'hello/to the/world'`) +t.equal(nixEscape(`hello/to%the/world`), `'hello/to%the/world'`) +t.equal(nixEscape(`hello/to'the/world`), `'hello/to'"'"'the/world'`)