Skip to content

Commit

Permalink
fix: correctly handle an unpaired quotes
Browse files Browse the repository at this point in the history
  • Loading branch information
mrmlnc committed Jan 7, 2025
1 parent f355674 commit a2e5787
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 17 deletions.
36 changes: 22 additions & 14 deletions lib/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,25 +165,33 @@ const parse = (input, options = {}) => {
*/

if (value === CHAR_DOUBLE_QUOTE || value === CHAR_SINGLE_QUOTE || value === CHAR_BACKTICK) {
const open = value;
let next;
const leftQuote = value;
// Looking for the nearest unescaped quote of the same type.
// @todo Use negative lookbehind after targeting the [email protected]+
const hasRightQuote = input.slice(index).search(new RegExp(`[^\\\\]${leftQuote}|^${leftQuote}`)) !== -1;

if (options.keepQuotes !== true) {
value = '';
}
let next;

while (index < length && (next = advance())) {
if (next === CHAR_BACKSLASH) {
value += next + advance();
continue;
// If there is no right quote, consume an unpaired quote as a regular character.
if (hasRightQuote) {
if (options.keepQuotes !== true) {
value = '';
}

if (next === open) {
if (options.keepQuotes === true) value += next;
break;
}
while (index <= length && (next = advance())) {
// Skip escaped quotes.
if (next === CHAR_BACKSLASH) {
value += next + advance();
continue;
}

value += next;
if (next === leftQuote) {
if (options.keepQuotes === true) value += next;
break;
}

value += next;
}
}

push({ type: 'text', value });
Expand Down
67 changes: 64 additions & 3 deletions test/bash-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,74 @@ const equal = (input, expected = bash(input), options) => {

describe('bash', () => {
const fixtures = [
['{1\\.2}', {}, ['{1.2}']],
['{1\\.2}', { keepEscaping: true }, ['{1\\.2}']],
// Single paired quotes
["''{x,y}", {}, ["x", "y"]],
["''{x,y}", { keepQuotes: true }, ["''x", "''y"]],
["a'b'c{x,y}", {}, ['abcx', 'abcy']],
["a'b'c{x,y}", { keepQuotes: true }, ["a'b'cx", "a'b'cy"]],
["'{x,x}'", {}, ['{x,x}']],
["'{x,x}'", { keepQuotes: true }, ["'{x,x}'"]],
["{'x,x'}", {}, ['{x,x}']],
["{'x,x'}", { keepQuotes: true }, ["{'x,x'}"]],
["{x','x}", {}, ['{x,x}']],
["{x','x}", { keepQuotes: true }, ["{x','x}"]],
// Single unpaired quotes
["'{x,y}", {}, ["'x", "'y"]],
["'{x,y}", { keepQuotes: true }, ["'x", "'y"]],
["a'bc{x,y}", {}, ["a'bcx", "a'bcy"]],
["a'bc{x,y}", { keepQuotes: true }, ["a'bcx", "a'bcy"]],
// Double paired quotes
['""{x,y}', {}, ['x', 'y']],
['""{x,y}', { keepQuotes: true }, ['""x', '""y']],
['a"b"c{x,y}', {}, ['abcx', 'abcy']],
['a"b"c{x,y}', { keepQuotes: true }, ['a"b"cx', 'a"b"cy']],
['"{x,x}"', {}, ['{x,x}']],
['"{x,x}"', { keepQuotes: true }, ['"{x,x}"']],
['{"x,x"}', {}, ['{x,x}']],
['{"x,x"}', { keepQuotes: true }, ['{"x,x"}']],
['{x","x}', {}, ['{x,x}']],
['\'{x,x}\'', {}, ['{x,x}']],
['{x","x}', { keepQuotes: true }, ['{x","x}']],
// Double unpaired quotes
['"{x,y}', {}, ['"x', '"y']],
['"{x,y}', { keepQuotes: true }, ['"x', '"y']],
['a"bc{x,y}', {}, ['a"bcx', 'a"bcy']],
['a"bc{x,y}', { keepQuotes: true }, ['a"bcx', 'a"bcy']],
// Paired backticks
['``{x,y}', {}, ['x', 'y']],
['``{x,y}', { keepQuotes: true }, ['``x', '``y']],
['a`b`c{x,y}', {}, ['abcx', 'abcy']],
['a`b`c{x,y}', { keepQuotes: true }, ['a`b`cx', 'a`b`cy']],
['`{x,x}`', {}, ['{x,x}']],
['`{x,x}`', { keepQuotes: true }, ['`{x,x}`']],
['{`x,x`}', {}, ['{x,x}']],
['{`x,x`}', { keepQuotes: true }, ['{`x,x`}']],
['{x`,`x}', {}, ['{x,x}']],
['{x`,`x}', { keepQuotes: true }, ['{x`,`x}']],
// Unpaired backticks
['`{x,y}', {}, ['`x', '`y']],
['`{x,y}', { keepQuotes: true }, ['`x', '`y']],
['a`bc{x,y}', {}, ['a`bcx', 'a`bcy']],
['a`bc{x,y}', { keepQuotes: true }, ['a`bcx', 'a`bcy']],
// Mixed unpaired quotes
['a\'b"c`{x,y}', {}, ['a\'b"c`x', 'a\'b"c`y']],
['a\'b"c`{x,y}', { keepQuotes: true }, ['a\'b"c`x', 'a\'b"c`y']],
// Mixed quotes
[`a\\'"'"b{x,y}`, {}, [`a''bx`, `a''by`]],
['a"\'`b`\'"c{x,y}', {}, ['a\'`b`\'cx', 'a\'`b`\'cy']],
['a"\'`b`\'"c{x,y}', { keepQuotes: true }, ['a"\'`b`\'"cx', 'a"\'`b`\'"cy']],
// Escaped quotes
["\\'{x,y}", {}, ["'x", "'y"]],
["\\'{x,y}", { keepEscaping: true }, ["\\'x", "\\'y"]],
["a'b{x,y}\\'", {}, ["a'bx'", "a'by'"]],
["a'b{x,y}\\'", { keepEscaping: true }, ["a'bx\\'", "a'by\\'"]],
["a'b{x,y}\\'", { keepQuotes: true }, ["a'bx'", "a'by'"]],
["a'b{x,y}\\'", { keepEscaping: true, keepQuotes: true }, ["a'bx\\'", "a'by\\'"]],
["a'b{x,y}\\''", {}, ["ab{x,y}\\'"]],
["a'b{x,y}\\''", { keepQuotes: true }, ["a'b{x,y}\\''"]],
["a'\\'b{x,y}", { keepQuotes: true }, ["a''bx", "a''by"]],
// Common
['{1\\.2}', {}, ['{1.2}']],
['{1\\.2}', { keepEscaping: true }, ['{1\\.2}']],
['\'{a,b}{{a,b},a,b}\'', {}, ['{a,b}{{a,b},a,b}']],
['A{b,{d,e},{f,g}}Z', {}, ['AbZ', 'AdZ', 'AeZ', 'AfZ', 'AgZ']],
['PRE-{a,b}{{a,b},a,b}-POST', {}, ['PRE-aa-POST', 'PRE-ab-POST', 'PRE-aa-POST', 'PRE-ab-POST', 'PRE-ba-POST', 'PRE-bb-POST', 'PRE-ba-POST', 'PRE-bb-POST']],
Expand Down

0 comments on commit a2e5787

Please sign in to comment.