From 62e9b4958cfa2f9009b7069076612fe33528c1fb Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 27 Oct 2022 21:41:09 -0700 Subject: [PATCH 1/8] [actions] update checkout action --- .github/workflows/rebase.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml index 1dea349..1818191 100644 --- a/.github/workflows/rebase.yml +++ b/.github/workflows/rebase.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: ljharb/rebase@master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 85f8e31dd80e1dde63d58204b653e497a53857e6 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 30 Jan 2023 14:16:22 -0800 Subject: [PATCH 2/8] [Dev Deps] update `@ljharb/eslint-config`, `aud`, `tape` --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index ca1283a..c6dc400 100644 --- a/package.json +++ b/package.json @@ -12,14 +12,14 @@ }, "bugs": "https://github.com/ljharb/shell-quote/issues", "devDependencies": { - "@ljharb/eslint-config": "^21.0.0", - "aud": "^2.0.1", + "@ljharb/eslint-config": "^21.0.1", + "aud": "^2.0.2", "auto-changelog": "^2.4.0", "eslint": "=8.8.0", "in-publish": "^2.0.1", "npmignore": "^0.3.0", "safe-publish-latest": "^2.0.0", - "tape": "^5.6.1" + "tape": "^5.6.3" }, "homepage": "https://github.com/ljharb/shell-quote", "keywords": [ From 553fdfc32cc41b4c2f77e061b6957703958ca575 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 30 Jan 2023 14:22:58 -0800 Subject: [PATCH 3/8] [New] extract `parse` and `quote` to their own deep imports --- README.md | 16 ++--- index.js | 203 +----------------------------------------------------- parse.js | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++ quote.js | 16 +++++ 4 files changed, 216 insertions(+), 209 deletions(-) create mode 100644 parse.js create mode 100644 quote.js diff --git a/README.md b/README.md index 3d86a2e..c2f6e08 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Parse and quote shell commands. ## quote ``` js -var quote = require('shell-quote').quote; +var quote = require('shell-quote/quote'); var s = quote([ 'a', 'b c d', '$f', '"g"' ]); console.log(s); ``` @@ -28,7 +28,7 @@ a 'b c d' \$f '"g"' ## parse ``` js -var parse = require('shell-quote').parse; +var parse = require('shell-quote/parse'); var xs = parse('a "b c" \\$def \'it\\\'s great\''); console.dir(xs); ``` @@ -42,7 +42,7 @@ output ## parse with an environment variable ``` js -var parse = require('shell-quote').parse; +var parse = require('shell-quote/parse'); var xs = parse('beep --boop="$PWD"', { PWD: '/home/robot' }); console.dir(xs); ``` @@ -56,7 +56,7 @@ output ## parse with custom escape character ``` js -var parse = require('shell-quote').parse; +var parse = require('shell-quote/parse'); var xs = parse('beep --boop="$PWD"', { PWD: '/home/robot' }, { escape: '^' }); console.dir(xs); ``` @@ -70,7 +70,7 @@ output ## parsing shell operators ``` js -var parse = require('shell-quote').parse; +var parse = require('shell-quote/parse'); var xs = parse('beep || boop > /byte'); console.dir(xs); ``` @@ -84,7 +84,7 @@ output: ## parsing shell comment ``` js -var parse = require('shell-quote').parse; +var parse = require('shell-quote/parse'); var xs = parse('beep > boop # > kaboom'); console.dir(xs); ``` @@ -98,8 +98,8 @@ output: # methods ``` js -var quote = require('shell-quote').quote; -var parse = require('shell-quote').parse; +var quote = require('shell-quote/quote'); +var parse = require('shell-quote/parse'); ``` ## quote(args) diff --git a/index.js b/index.js index 0a9ae2d..28fb42d 100644 --- a/index.js +++ b/index.js @@ -1,203 +1,4 @@ 'use strict'; -exports.quote = function (xs) { - return xs.map(function (s) { - if (s && typeof s === 'object') { - return s.op.replace(/(.)/g, '\\$1'); - } else if ((/["\s]/).test(s) && !(/'/).test(s)) { - return "'" + s.replace(/(['\\])/g, '\\$1') + "'"; - } else if ((/["'\s]/).test(s)) { - return '"' + s.replace(/(["\\$`!])/g, '\\$1') + '"'; - } - return String(s).replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}])/g, '$1\\$2'); - }).join(' '); -}; - -// '<(' is process substitution operator and -// can be parsed the same as control operator -var CONTROL = '(?:' + [ - '\\|\\|', '\\&\\&', ';;', '\\|\\&', '\\<\\(', '>>', '>\\&', '[&;()|<>]' -].join('|') + ')'; -var META = '|&;()<> \\t'; -var BAREWORD = '(\\\\[\'"' + META + ']|[^\\s\'"' + META + '])+'; -var SINGLE_QUOTE = '"((\\\\"|[^"])*?)"'; -var DOUBLE_QUOTE = '\'((\\\\\'|[^\'])*?)\''; - -var TOKEN = ''; -for (var i = 0; i < 4; i++) { - TOKEN += (Math.pow(16, 8) * Math.random()).toString(16); -} - -function parse(s, env, opts) { - var chunker = new RegExp([ - '(' + CONTROL + ')', // control chars - '(' + BAREWORD + '|' + SINGLE_QUOTE + '|' + DOUBLE_QUOTE + ')*' - ].join('|'), 'g'); - var match = s.match(chunker).filter(Boolean); - - if (!match) { - return []; - } - if (!env) { - env = {}; - } - if (!opts) { - opts = {}; - } - - var commented = false; - - function getVar(_, pre, key) { - var r = typeof env === 'function' ? env(key) : env[key]; - if (r === undefined && key != '') { - r = ''; - } else if (r === undefined) { - r = '$'; - } - - if (typeof r === 'object') { - return pre + TOKEN + JSON.stringify(r) + TOKEN; - } - return pre + r; - } - - return match.map(function (s, j) { - if (commented) { - return void undefined; - } - if (RegExp('^' + CONTROL + '$').test(s)) { - return { op: s }; - } - - // Hand-written scanner/parser for Bash quoting rules: - // - // 1. inside single quotes, all characters are printed literally. - // 2. inside double quotes, all characters are printed literally - // except variables prefixed by '$' and backslashes followed by - // either a double quote or another backslash. - // 3. outside of any quotes, backslashes are treated as escape - // characters and not printed (unless they are themselves escaped) - // 4. quote context can switch mid-token if there is no whitespace - // between the two quote contexts (e.g. all'one'"token" parses as - // "allonetoken") - var SQ = "'"; - var DQ = '"'; - var DS = '$'; - var BS = opts.escape || '\\'; - var quote = false; - var esc = false; - var out = ''; - var isGlob = false; - var i; - - function parseEnvVar() { - i += 1; - var varend; - var varname; - // debugger - if (s.charAt(i) === '{') { - i += 1; - if (s.charAt(i) === '}') { - throw new Error('Bad substitution: ' + s.substr(i - 2, 3)); - } - varend = s.indexOf('}', i); - if (varend < 0) { - throw new Error('Bad substitution: ' + s.substr(i)); - } - varname = s.substr(i, varend - i); - i = varend; - } else if ((/[*@#?$!_-]/).test(s.charAt(i))) { - varname = s.charAt(i); - i += 1; - } else { - varend = s.substr(i).match(/[^\w\d_]/); - if (!varend) { - varname = s.substr(i); - i = s.length; - } else { - varname = s.substr(i, varend.index); - i += varend.index - 1; - } - } - return getVar(null, '', varname); - } - - for (i = 0; i < s.length; i++) { - var c = s.charAt(i); - isGlob = isGlob || (!quote && (c === '*' || c === '?')); - if (esc) { - out += c; - esc = false; - } else if (quote) { - if (c === quote) { - quote = false; - } else if (quote == SQ) { - out += c; - } else { // Double quote - if (c === BS) { - i += 1; - c = s.charAt(i); - if (c === DQ || c === BS || c === DS) { - out += c; - } else { - out += BS + c; - } - } else if (c === DS) { - out += parseEnvVar(); - } else { - out += c; - } - } - } else if (c === DQ || c === SQ) { - quote = c; - } else if (RegExp('^' + CONTROL + '$').test(c)) { - return { op: s }; - } else if ((/^#$/).test(c)) { - commented = true; - if (out.length) { - return [out, { comment: s.slice(i + 1) + match.slice(j + 1).join(' ') }]; - } - return [{ comment: s.slice(i + 1) + match.slice(j + 1).join(' ') }]; - } else if (c === BS) { - esc = true; - } else if (c === DS) { - out += parseEnvVar(); - } else { - out += c; - } - } - - if (isGlob) { - return { op: 'glob', pattern: out }; - } - - return out; - }).reduce(function (prev, arg) { // finalize parsed aruments - if (arg === undefined) { - return prev; - } - return prev.concat(arg); - }, []); -} - -exports.parse = function (s, env, opts) { - var mapped = parse(s, env, opts); - if (typeof env !== 'function') { - return mapped; - } - return mapped.reduce(function (acc, s) { - if (typeof s === 'object') { - return acc.concat(s); - } - var xs = s.split(RegExp('(' + TOKEN + '.*?' + TOKEN + ')', 'g')); - if (xs.length === 1) { - return acc.concat(xs[0]); - } - return acc.concat(xs.filter(Boolean).map(function (x) { - if (RegExp('^' + TOKEN).test(x)) { - return JSON.parse(x.split(TOKEN)[1]); - } - return x; - })); - }, []); -}; +exports.quote = require('./quote'); +exports.parse = require('./parse'); diff --git a/parse.js b/parse.js new file mode 100644 index 0000000..3ea9c1b --- /dev/null +++ b/parse.js @@ -0,0 +1,190 @@ +'use strict'; + +// '<(' is process substitution operator and +// can be parsed the same as control operator +var CONTROL = '(?:' + [ + '\\|\\|', '\\&\\&', ';;', '\\|\\&', '\\<\\(', '>>', '>\\&', '[&;()|<>]' +].join('|') + ')'; +var META = '|&;()<> \\t'; +var BAREWORD = '(\\\\[\'"' + META + ']|[^\\s\'"' + META + '])+'; +var SINGLE_QUOTE = '"((\\\\"|[^"])*?)"'; +var DOUBLE_QUOTE = '\'((\\\\\'|[^\'])*?)\''; + +var TOKEN = ''; +for (var i = 0; i < 4; i++) { + TOKEN += (Math.pow(16, 8) * Math.random()).toString(16); +} + +function parseInternal(s, env, opts) { + var chunker = new RegExp([ + '(' + CONTROL + ')', // control chars + '(' + BAREWORD + '|' + SINGLE_QUOTE + '|' + DOUBLE_QUOTE + ')*' + ].join('|'), 'g'); + var match = s.match(chunker).filter(Boolean); + + if (!match) { + return []; + } + if (!env) { + env = {}; + } + if (!opts) { + opts = {}; + } + + var commented = false; + + function getVar(_, pre, key) { + var r = typeof env === 'function' ? env(key) : env[key]; + if (r === undefined && key != '') { + r = ''; + } else if (r === undefined) { + r = '$'; + } + + if (typeof r === 'object') { + return pre + TOKEN + JSON.stringify(r) + TOKEN; + } + return pre + r; + } + + return match.map(function (s, j) { + if (commented) { + return void undefined; + } + if (RegExp('^' + CONTROL + '$').test(s)) { + return { op: s }; + } + + // Hand-written scanner/parser for Bash quoting rules: + // + // 1. inside single quotes, all characters are printed literally. + // 2. inside double quotes, all characters are printed literally + // except variables prefixed by '$' and backslashes followed by + // either a double quote or another backslash. + // 3. outside of any quotes, backslashes are treated as escape + // characters and not printed (unless they are themselves escaped) + // 4. quote context can switch mid-token if there is no whitespace + // between the two quote contexts (e.g. all'one'"token" parses as + // "allonetoken") + var SQ = "'"; + var DQ = '"'; + var DS = '$'; + var BS = opts.escape || '\\'; + var quote = false; + var esc = false; + var out = ''; + var isGlob = false; + var i; + + function parseEnvVar() { + i += 1; + var varend; + var varname; + // debugger + if (s.charAt(i) === '{') { + i += 1; + if (s.charAt(i) === '}') { + throw new Error('Bad substitution: ' + s.substr(i - 2, 3)); + } + varend = s.indexOf('}', i); + if (varend < 0) { + throw new Error('Bad substitution: ' + s.substr(i)); + } + varname = s.substr(i, varend - i); + i = varend; + } else if ((/[*@#?$!_-]/).test(s.charAt(i))) { + varname = s.charAt(i); + i += 1; + } else { + varend = s.substr(i).match(/[^\w\d_]/); + if (!varend) { + varname = s.substr(i); + i = s.length; + } else { + varname = s.substr(i, varend.index); + i += varend.index - 1; + } + } + return getVar(null, '', varname); + } + + for (i = 0; i < s.length; i++) { + var c = s.charAt(i); + isGlob = isGlob || (!quote && (c === '*' || c === '?')); + if (esc) { + out += c; + esc = false; + } else if (quote) { + if (c === quote) { + quote = false; + } else if (quote == SQ) { + out += c; + } else { // Double quote + if (c === BS) { + i += 1; + c = s.charAt(i); + if (c === DQ || c === BS || c === DS) { + out += c; + } else { + out += BS + c; + } + } else if (c === DS) { + out += parseEnvVar(); + } else { + out += c; + } + } + } else if (c === DQ || c === SQ) { + quote = c; + } else if (RegExp('^' + CONTROL + '$').test(c)) { + return { op: s }; + } else if ((/^#$/).test(c)) { + commented = true; + if (out.length) { + return [out, { comment: s.slice(i + 1) + match.slice(j + 1).join(' ') }]; + } + return [{ comment: s.slice(i + 1) + match.slice(j + 1).join(' ') }]; + } else if (c === BS) { + esc = true; + } else if (c === DS) { + out += parseEnvVar(); + } else { + out += c; + } + } + + if (isGlob) { + return { op: 'glob', pattern: out }; + } + + return out; + }).reduce(function (prev, arg) { // finalize parsed aruments + if (arg === undefined) { + return prev; + } + return prev.concat(arg); + }, []); +} + +module.exports = function parse(s, env, opts) { + var mapped = parseInternal(s, env, opts); + if (typeof env !== 'function') { + return mapped; + } + return mapped.reduce(function (acc, s) { + if (typeof s === 'object') { + return acc.concat(s); + } + var xs = s.split(RegExp('(' + TOKEN + '.*?' + TOKEN + ')', 'g')); + if (xs.length === 1) { + return acc.concat(xs[0]); + } + return acc.concat(xs.filter(Boolean).map(function (x) { + if (RegExp('^' + TOKEN).test(x)) { + return JSON.parse(x.split(TOKEN)[1]); + } + return x; + })); + }, []); +}; diff --git a/quote.js b/quote.js new file mode 100644 index 0000000..afbccf0 --- /dev/null +++ b/quote.js @@ -0,0 +1,16 @@ +'use strict'; + +module.exports = function quote(xs) { + return xs.map(function (s) { + if (s && typeof s === 'object') { + return s.op.replace(/(.)/g, '\\$1'); + } + if ((/["\s]/).test(s) && !(/'/).test(s)) { + return "'" + s.replace(/(['\\])/g, '\\$1') + "'"; + } + if ((/["'\s]/).test(s)) { + return '"' + s.replace(/(["\\$`!])/g, '\\$1') + '"'; + } + return String(s).replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}])/g, '$1\\$2'); + }).join(' '); +}; From c5549fcd82d70046bdc2b1c34184ae9f9d0191f9 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 30 Jan 2023 14:24:55 -0800 Subject: [PATCH 4/8] [Tests] add `evalmd` --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index c6dc400..d0fdc46 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "aud": "^2.0.2", "auto-changelog": "^2.4.0", "eslint": "=8.8.0", + "evalmd": "^0.0.19", "in-publish": "^2.0.1", "npmignore": "^0.3.0", "safe-publish-latest": "^2.0.0", @@ -38,6 +39,7 @@ "prepack": "npmignore --auto --commentLines=autogenerated", "prepublish": "not-in-publish || npm run prepublishOnly", "prepublishOnly": "safe-publish-latest", + "prelint": "evalmd README.md", "lint": "eslint --ext=js,mjs .", "pretest": "npm run lint", "tests-only": "tape 'test/**/*.js'", From 216b19894f76b14d164c4c5a68f05a51b06336c4 Mon Sep 17 00:00:00 2001 From: Lasse Koskela Date: Sun, 29 Jan 2023 10:42:40 +0200 Subject: [PATCH 5/8] [New] `parse`: Add syntax support for duplicating input file descriptors --- parse.js | 2 +- test/op.js | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/parse.js b/parse.js index 3ea9c1b..f4c46a7 100644 --- a/parse.js +++ b/parse.js @@ -3,7 +3,7 @@ // '<(' is process substitution operator and // can be parsed the same as control operator var CONTROL = '(?:' + [ - '\\|\\|', '\\&\\&', ';;', '\\|\\&', '\\<\\(', '>>', '>\\&', '[&;()|<>]' + '\\|\\|', '\\&\\&', ';;', '\\|\\&', '\\<\\(', '>>', '>\\&', '<\\&', '[&;()|<>]' ].join('|') + ')'; var META = '|&;()<> \\t'; var BAREWORD = '(\\\\[\'"' + META + ']|[^\\s\'"' + META + '])+'; diff --git a/test/op.js b/test/op.js index 5fdce9e..d665f11 100644 --- a/test/op.js +++ b/test/op.js @@ -69,6 +69,19 @@ test('double operators', function (t) { t.end(); }); +test('duplicating input file descriptors', function (t) { + // duplicating stdout to file descriptor 3 + t.same(parse('beep 3<&1'), ['beep', '3', { op: '<&' }, '1']); + + // duplicating stdout to file descriptor 0, i.e. stdin + t.same(parse('beep <&1'), ['beep', { op: '<&' }, '1']); + + // closes stdin + t.same(parse('beep <&-'), ['beep', { op: '<&' }, '-']); + + t.end(); +}); + test('glob patterns', function (t) { t.same( parse('tap test/*.test.js'), From 9802fb37c7946e18c672b81122520dc296bde271 Mon Sep 17 00:00:00 2001 From: Lasse Koskela Date: Sun, 29 Jan 2023 10:11:00 +0200 Subject: [PATCH 6/8] [New] Add support for here strings (`<<<`) --- parse.js | 2 +- test/env.js | 9 +++++++++ test/op.js | 9 +++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/parse.js b/parse.js index f4c46a7..1fff1bc 100644 --- a/parse.js +++ b/parse.js @@ -3,7 +3,7 @@ // '<(' is process substitution operator and // can be parsed the same as control operator var CONTROL = '(?:' + [ - '\\|\\|', '\\&\\&', ';;', '\\|\\&', '\\<\\(', '>>', '>\\&', '<\\&', '[&;()|<>]' + '\\|\\|', '\\&\\&', ';;', '\\|\\&', '\\<\\(', '\\<\\<\\<', '>>', '>\\&', '<\\&', '[&;()|<>]' ].join('|') + ')'; var META = '|&;()<> \\t'; var BAREWORD = '(\\\\[\'"' + META + ']|[^\\s\'"' + META + '])+'; diff --git a/test/env.js b/test/env.js index deb854b..4cc0a51 100644 --- a/test/env.js +++ b/test/env.js @@ -23,6 +23,15 @@ test('expand environment variables', function (t) { t.end(); }); +test('expand environment variables within here-strings', function (t) { + t.same(parse('a <<< $x', { x: 'Joe' }), ['a', { op: '<<<' }, 'Joe']); + t.same(parse('a <<< ${x}', { x: 'Joe' }), ['a', { op: '<<<' }, 'Joe']); + t.same(parse('a <<< "$x"', { x: 'Joe' }), ['a', { op: '<<<' }, 'Joe']); + t.same(parse('a <<< "${x}"', { x: 'Joe' }), ['a', { op: '<<<' }, 'Joe']); + + t.end(); +}); + test('environment variables with metacharacters', function (t) { t.same(parse('a $XYZ c', { XYZ: '"b"' }), ['a', '"b"', 'c']); t.same(parse('a $XYZ c', { XYZ: '$X', X: 5 }), ['a', '$X', 'c']); diff --git a/test/op.js b/test/op.js index d665f11..38d3757 100644 --- a/test/op.js +++ b/test/op.js @@ -82,6 +82,15 @@ test('duplicating input file descriptors', function (t) { t.end(); }); +test('here strings', function (t) { + t.same(parse('cat <<< "hello world"'), ['cat', { op: '<<<' }, 'hello world']); + t.same(parse('cat <<< hello'), ['cat', { op: '<<<' }, 'hello']); + t.same(parse('cat<< Date: Mon, 30 Jan 2023 15:50:10 -0800 Subject: [PATCH 7/8] [Tests] add `nyc` coverage --- .github/workflows/nodejs.yml | 8 ++++---- .gitignore | 3 +++ .nycrc | 14 ++++++++++++++ package.json | 3 ++- 4 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 .nycrc diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 211920c..f485047 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -57,9 +57,9 @@ jobs: NPM_CONFIG_STRICT_SSL: false if: matrix.os == 'windows-latest' - - run: npm run tests-only + - run: ./node_modules/.bin/tape 'test/**/*.js' - uses: codecov/codecov-action@v3 - + nonlatest: needs: [matrix, latest] name: 'non-latest majors' @@ -97,7 +97,7 @@ jobs: NPM_CONFIG_STRICT_SSL: false if: matrix.os == 'windows-latest' - - run: npm run tests-only + - run: ./node_modules/.bin/tape 'test/**/*.js' - uses: codecov/codecov-action@v3 node: @@ -105,4 +105,4 @@ jobs: needs: [latest, nonlatest] runs-on: ubuntu-latest steps: - - run: 'echo tests completed' \ No newline at end of file + - run: 'echo tests completed' diff --git a/.gitignore b/.gitignore index da9c2fc..0cfeaf4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ node_modules +coverage/ +.nyc_output/ + # Only apps should have lockfiles npm-shrinkwrap.json package-lock.json diff --git a/.nycrc b/.nycrc new file mode 100644 index 0000000..55c3d29 --- /dev/null +++ b/.nycrc @@ -0,0 +1,14 @@ +{ + "all": true, + "check-coverage": false, + "reporter": ["text-summary", "text", "html", "json"], + "lines": 86, + "statements": 85.93, + "functions": 82.43, + "branches": 76.06, + "exclude": [ + "coverage", + "example", + "test" + ] +} diff --git a/package.json b/package.json index d0fdc46..74a7997 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "evalmd": "^0.0.19", "in-publish": "^2.0.1", "npmignore": "^0.3.0", + "nyc": "^10.3.2", "safe-publish-latest": "^2.0.0", "tape": "^5.6.3" }, @@ -42,7 +43,7 @@ "prelint": "evalmd README.md", "lint": "eslint --ext=js,mjs .", "pretest": "npm run lint", - "tests-only": "tape 'test/**/*.js'", + "tests-only": "nyc tape 'test/**/*.js'", "test": "npm run tests-only", "posttest": "aud --production", "version": "auto-changelog && git add CHANGELOG.md", From 508e2f9c6439706cc696407d52fec36f78248a19 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 30 Jan 2023 19:26:21 -0800 Subject: [PATCH 8/8] v1.8.0 --- CHANGELOG.md | 12 ++++++++++++ package.json | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df542b6..c63d281 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v1.8.0](https://github.com/ljharb/shell-quote/compare/v1.7.4...v1.8.0) - 2023-01-30 + +### Commits + +- [New] extract `parse` and `quote` to their own deep imports [`553fdfc`](https://github.com/ljharb/shell-quote/commit/553fdfc32cc41b4c2f77e061b6957703958ca575) +- [Tests] add `nyc` coverage [`fd7ddcd`](https://github.com/ljharb/shell-quote/commit/fd7ddcdd84bfef064c6d9a06b055a95531b26897) +- [New] Add support for here strings (`<<<`) [`9802fb3`](https://github.com/ljharb/shell-quote/commit/9802fb37c7946e18c672b81122520dc296bde271) +- [New] `parse`: Add syntax support for duplicating input file descriptors [`216b198`](https://github.com/ljharb/shell-quote/commit/216b19894f76b14d164c4c5a68f05a51b06336c4) +- [Dev Deps] update `@ljharb/eslint-config`, `aud`, `tape` [`85f8e31`](https://github.com/ljharb/shell-quote/commit/85f8e31dd80e1dde63d58204b653e497a53857e6) +- [Tests] add `evalmd` [`c5549fc`](https://github.com/ljharb/shell-quote/commit/c5549fcd82d70046bdc2b1c34184ae9f9d0191f9) +- [actions] update checkout action [`62e9b49`](https://github.com/ljharb/shell-quote/commit/62e9b4958cfa2f9009b7069076612fe33528c1fb) + ## [v1.7.4](https://github.com/ljharb/shell-quote/compare/1.7.3...v1.7.4) - 2022-10-12 ### Merged diff --git a/package.json b/package.json index 74a7997..945d5e3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "shell-quote", "description": "quote and parse shell commands", - "version": "1.7.4", + "version": "1.8.0", "author": { "name": "James Halliday", "email": "mail@substack.net",