Skip to content

Commit

Permalink
quote empty strings on Unix (shell) (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
chocolateboy committed Feb 25, 2020
1 parent 5245a85 commit 4af1063
Show file tree
Hide file tree
Showing 9 changed files with 1,371 additions and 1,642 deletions.
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
## 3.0.0 - TBD

- fix [#GH-12](https://github.com/chocolateboy/shell-escape-tag/issues/12) - thanks, Ada Cohen

Correctly quote empty strings on Unix (shell). This is a bug fix rather than a
breaking change, but released as a new major version in case anyone was relying
on the broken behavior, which has been there since the start.

### before

```javascript
const pattern = ''
shell`echo foo | grep ${pattern}` // => echo foo | grep
```

### after

```javascript
const pattern = ''
shell`echo foo | grep ${pattern}` // => echo foo | grep ''
```

- update dependencies

## 2.0.2 - 2019-11-17

- add tests for the examples in the README
Expand Down
32 changes: 15 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,32 @@
"license": "Artistic-2.0",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"files": [
"dist/index.js",
"dist/index.esm.js"
],
"scripts": {
"build": "cross-env NODE_ENV=production bili --format cjs --format esm src/*.js",
"build": "bili --quiet --map --format cjs -d dist src/index.js",
"build:prod": "cross-env NODE_ENV=production bili --format cjs --format esm -d dist src/index.js",
"clean": "shx rm -rf dist",
"doctoc": "markdown-toc --bullets=- -i README.md",
"prepublishOnly": "run-s clean build:prod test:unit",
"rebuild": "run-s clean build",
"test:run": "ava --verbose",
"test": "run-s rebuild test:run"
},
"ava": {
"files": [
"test/*.js"
]
"repl": "node -r ./dev/repl.js",
"test": "run-s build test:unit",
"test:unit": "ava --verbose"
},
"files": [
"dist/index.esm.js",
"dist/index.js"
],
"dependencies": {
"any-shell-escape": "^0.1.1",
"any-shell-escape": "chocolateboy/any-shell-escape#quote-empty-strings",
"inspect-custom-symbol": "^1.1.1",
"just-flatten-it": "^2.1.0",
"just-zip-it": "^2.1.0"
},
"devDependencies": {
"@babel/preset-env": "^7.7.1",
"ava": "^2.4.0",
"@babel/preset-env": "^7.8.4",
"ava": "^3.4.0",
"bili": "^4.8.1",
"cross-env": "^6.0.3",
"cross-env": "^7.0.0",
"npm-run-all": "^4.1.5",
"shx": "^0.3.2"
},
Expand All @@ -45,7 +44,6 @@
"quote",
"shell",
"tag",
"tags",
"tagged",
"tagged-template",
"tagged-templates",
Expand Down
4 changes: 2 additions & 2 deletions test/examples.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import shell from '..'
import test from 'ava'
const test = require('ava')
const shell = require('..')

// XXX results assume these tests are running on non-Windows

Expand Down
35 changes: 35 additions & 0 deletions test/quote-empty-strings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const test = require('ava')
const shell = require('..')

// from https://github.com/chocolateboy/shell-escape-tag/issues/12

test('grep', t => {
const pattern = ''
const command = shell`echo 'hello' | grep ${pattern}`

t.is(command, "echo 'hello' | grep ''")
})

test('shell', t => {
const emptyString = ''
const emptyStrings = ['foo', '', 'bar', '', 'baz']
const command = shell`command --test ${emptyString} ${emptyStrings}`

t.is(command, "command --test '' foo '' bar '' baz")
})

test('escape', t => {
const emptyString = shell.escape('')
const emptyStrings = shell.escape(['foo', '', 'bar', '', 'baz'])
const command = shell`command --test ${emptyString} ${emptyStrings}`

t.is(command, "command --test '' foo '' bar '' baz")
})

test('preserve', t => {
const emptyString = shell.preserve('')
const emptyStrings = shell.preserve(['foo', '', 'bar', '', 'baz'])
const command = shell`command --test ${emptyString} ${emptyStrings}`

t.is(command, 'command --test foo bar baz')
})
2 changes: 1 addition & 1 deletion test/require.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import test from 'ava'
const test = require('ava')

test('require works without .default', t => {
const shell = require('..')
Expand Down
24 changes: 12 additions & 12 deletions test/shell.escape.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import shell from '..'
import test from 'ava'
import { inspect } from 'util'
const test = require('ava')
const { inspect } = require('util')
const shell = require('..')

test('stringifies to the escaped string', t => {
t.is(shell.escape('Foo Bar').toString(), "'Foo Bar'")
Expand All @@ -13,7 +13,7 @@ test('escapes a string with spaces', t => {

test('escapes an array of strings with spaces', t => {
t.is(
shell.escape([ 'Foo Bar', 'Baz Quux' ]).value,
shell.escape(['Foo Bar', 'Baz Quux']).value,
"'Foo Bar' 'Baz Quux'"
)
})
Expand All @@ -27,7 +27,7 @@ test('escapes a string with quotes', t => {

test('escapes an array of strings with quotes', t => {
t.is(
shell.escape([ `Foo's "Bar"`, `Foo 'Bar' "Baz"` ]).value,
shell.escape([`Foo's "Bar"`, `Foo 'Bar' "Baz"`]).value,
`'Foo'"'"'s "Bar"' 'Foo '"'"'Bar'"'"' "Baz"'`
)
})
Expand All @@ -50,7 +50,7 @@ test('ignores null and undefined array values', t => {
})

test('stringifies defined values', t => {
t.is(shell.escape('').value, '')
t.is(shell.escape('').value, "''")
t.is(shell.escape(false).value, 'false')
t.is(shell.escape(42).value, '42')
})
Expand All @@ -64,7 +64,7 @@ test('stringifies defined array values', t => {
false,
'Baz Quux'
]).value,
`'Foo Bar' 0 false 'Baz Quux'`
`'Foo Bar' 0 '' false 'Baz Quux'`
)
})

Expand All @@ -74,12 +74,12 @@ test('flattens nested array values', t => {
[ 'Foo Bar',
[ 0, '', false,
[ null, undefined,
[ 'Baz Quux' ]
['Baz Quux']
]
]
]
]).value,
`'Foo Bar' 0 false 'Baz Quux'`
`'Foo Bar' 0 '' false 'Baz Quux'`
)
})

Expand All @@ -89,15 +89,15 @@ test("doesn't escape embedded escaped strings", t => {
})

test("doesn't escape embedded escaped arrays", t => {
const escaped = shell.escape([ 'Foo Bar', 'Baz Quux' ])
const escaped = shell.escape(['Foo Bar', 'Baz Quux'])
t.is(shell`foo ${escaped}`, "foo 'Foo Bar' 'Baz Quux'")
})

test("doesn't escape embedded nested strings/arrays", t => {
const escaped = shell.escape([
'1 2',
shell.escape('3 4'),
shell.escape([ '5 6', '7 8' ]),
shell.escape(['5 6', '7 8']),
'9 10'
])

Expand All @@ -108,7 +108,7 @@ test("doesn't escape embedded nested strings/arrays", t => {
})

test('supports embedded preserves', t => {
const param = shell.escape(shell.preserve('foo bar'), [ "baz's quux" ])
const param = shell.escape(shell.preserve('foo bar'), ["baz's quux"])

t.is(
shell`command ${param}`,
Expand Down
28 changes: 14 additions & 14 deletions test/shell.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import shell from '..'
import test from 'ava'
const test = require('ava')
const shell = require('..')

test('escapes an empty string', t => {
test('preserves strings with no interpolation', t => {
t.is(shell``, '')
t.is(shell` `, ' ')
t.is(shell`\t`, '\t')
t.is(shell`\n`, '\n')
t.is(shell`\r`, '\r')
t.is(shell`foo bar`, 'foo bar')
t.is(shell`foo's bar`, `foo's bar`)
})

test('ignores null values', t => {
Expand All @@ -14,19 +20,13 @@ test('ignores undefined values', t => {
})

test('ignores null and undefined values', t => {
const bar = [ null, undefined, 'bar', undefined, null ]
const bar = [null, undefined, 'bar', undefined, null]
t.is(shell`foo${bar}baz`, 'foobarbaz')
})

test('does not ignore empty strings', t => {
const bar = [ '', 'bar', '' ]
t.is(shell`foo${bar}baz`, 'foo bar baz')
})

test('escapes a string with no interpolations', t => {
t.is(shell`foo`, 'foo')
t.is(shell`foo bar`, 'foo bar')
t.is(shell`foo bar baz`, 'foo bar baz')
const bar = ['', 'bar', '']
t.is(shell`foo ${bar} baz`, "foo '' bar '' baz")
})

test('escapes a string which only contains an interpolation', t => {
Expand Down Expand Up @@ -80,7 +80,7 @@ test('escapes a string with spaces', t => {
})

test('escapes an array of strings with spaces', t => {
const foo = [ 'Foo Bar', 'Baz Quux' ]
const foo = ['Foo Bar', 'Baz Quux']
t.is(shell`foo ${foo}`, "foo 'Foo Bar' 'Baz Quux'")
})

Expand All @@ -94,7 +94,7 @@ test('escapes a string with quotes', t => {
})

test('escapes an array of strings with quotes', t => {
const foo = [ `Foo's "Bar"`, `Foo 'Bar' "Baz"` ]
const foo = [`Foo's "Bar"`, `Foo 'Bar' "Baz"`]

t.is(
shell`foo ${foo} bar`,
Expand Down
18 changes: 9 additions & 9 deletions test/shell.preserve.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import shell from '..'
import test from 'ava'
import { inspect } from 'util'
const test = require('ava')
const { inspect } = require('util')
const shell = require('..')

test('is aliased to shell.protect', t => {
t.is(shell.protect, shell.preserve)
Expand All @@ -21,7 +21,7 @@ test('preserves a string with spaces', t => {

test('preserves an array of strings with spaces', t => {
t.is(
shell.preserve([ 'Foo Bar', 'Baz Quux' ]).value,
shell.preserve(['Foo Bar', 'Baz Quux']).value,
'Foo Bar Baz Quux'
)
})
Expand All @@ -35,7 +35,7 @@ test('preserves a string with quotes', t => {

test('preserves an array of strings with quotes', t => {
t.is(
shell.preserve([ `Foo's "Bar"`, `Foo 'Bar' "Baz"` ]).value,
shell.preserve([`Foo's "Bar"`, `Foo 'Bar' "Baz"`]).value,
`Foo's "Bar" Foo 'Bar' "Baz"`
)
})
Expand Down Expand Up @@ -82,7 +82,7 @@ test('flattens nested array values', t => {
[ 'Foo Bar',
[ 0, '', false,
[ null, undefined,
[ 'Baz Quux' ]
['Baz Quux']
]
]
]
Expand All @@ -97,15 +97,15 @@ test('preserves embedded strings', t => {
})

test('preserves embedded arrays', t => {
const verbatim = shell.preserve([ 'Foo Bar', 'Baz Quux' ])
const verbatim = shell.preserve(['Foo Bar', 'Baz Quux'])
t.is(shell`foo ${verbatim}`, 'foo Foo Bar Baz Quux')
})

test('preserves embedded nested strings/arrays', t => {
const verbatim = shell.preserve([
'1 2',
shell.preserve('3 4'),
shell.preserve([ '5 6', '7 8' ]),
shell.preserve(['5 6', '7 8']),
'9 10'
])

Expand All @@ -116,7 +116,7 @@ test('preserves embedded nested strings/arrays', t => {
})

test('supports embedded escapes', t => {
const param = shell.preserve(shell.escape('foo bar'), [ "baz's quux" ])
const param = shell.preserve(shell.escape('foo bar'), ["baz's quux"])

t.is(
shell`command ${param}`,
Expand Down
Loading

0 comments on commit 4af1063

Please sign in to comment.