New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix argument concatenation with $
#553
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,21 +76,45 @@ const parseExpression = expression => { | |
throw new TypeError(`Unexpected "${typeOfExpression}" in template expression`); | ||
}; | ||
|
||
const parseTemplate = (template, index, templates, expressions) => { | ||
const concatTokens = (tokens, nextTokens, isNew) => isNew || tokens.length === 0 || nextTokens.length === 0 | ||
? [...tokens, ...nextTokens] | ||
: [ | ||
...tokens.slice(0, -1), | ||
`${tokens[tokens.length - 1]}${nextTokens[0]}`, | ||
...nextTokens.slice(1), | ||
]; | ||
|
||
const parseTemplate = ({templates, expressions, tokens, index, template}) => { | ||
const templateString = template ?? templates.raw[index]; | ||
const templateTokens = templateString.split(SPACES_REGEXP).filter(Boolean); | ||
const newTokens = concatTokens( | ||
tokens, | ||
templateTokens, | ||
templateString.startsWith(' '), | ||
); | ||
|
||
if (index === expressions.length) { | ||
return templateTokens; | ||
return newTokens; | ||
} | ||
|
||
const expression = expressions[index]; | ||
const expressionTokens = Array.isArray(expression) | ||
? expression.map(expression => parseExpression(expression)) | ||
: [parseExpression(expression)]; | ||
return concatTokens( | ||
newTokens, | ||
expressionTokens, | ||
templateString.endsWith(' '), | ||
); | ||
}; | ||
|
||
export const parseTemplates = (templates, expressions) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could use |
||
let tokens = []; | ||
|
||
return Array.isArray(expression) | ||
? [...templateTokens, ...expression.map(expression => parseExpression(expression))] | ||
: [...templateTokens, parseExpression(expression)]; | ||
for (const [index, template] of templates.entries()) { | ||
tokens = parseTemplate({templates, expressions, tokens, index, template}); | ||
} | ||
|
||
return tokens; | ||
}; | ||
|
||
export const parseTemplates = (templates, expressions) => templates.flatMap( | ||
(template, index) => parseTemplate(template, index, templates, expressions), | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -112,6 +112,11 @@ test('$ allows array interpolation', async t => { | |
t.is(stdout, 'foo\nbar'); | ||
}); | ||
|
||
test('$ allows empty array interpolation', async t => { | ||
const {stdout} = await $`echo.js foo ${[]} bar`; | ||
t.is(stdout, 'foo\nbar'); | ||
}); | ||
|
||
test('$ allows execa return value interpolation', async t => { | ||
const foo = await $`echo.js foo`; | ||
const {stdout} = await $`echo.js ${foo} bar`; | ||
|
@@ -171,6 +176,46 @@ test('$ handles invalid escape sequence', async t => { | |
t.is(stdout, '\\u'); | ||
}); | ||
|
||
test('$ can concatenate at the end of tokens', async t => { | ||
const {stdout} = await $`echo.js foo${'bar'}`; | ||
t.is(stdout, 'foobar'); | ||
}); | ||
|
||
test('$ does not concatenate at the end of tokens with a space', async t => { | ||
const {stdout} = await $`echo.js foo ${'bar'}`; | ||
t.is(stdout, 'foo\nbar'); | ||
}); | ||
|
||
test('$ can concatenate at the end of tokens followed by an array', async t => { | ||
const {stdout} = await $`echo.js foo${['bar', 'foo']}`; | ||
t.is(stdout, 'foobar\nfoo'); | ||
}); | ||
|
||
test('$ can concatenate at the start of tokens', async t => { | ||
const {stdout} = await $`echo.js ${'foo'}bar`; | ||
t.is(stdout, 'foobar'); | ||
}); | ||
|
||
test('$ does not concatenate at the start of tokens with a space', async t => { | ||
const {stdout} = await $`echo.js ${'foo'} bar`; | ||
t.is(stdout, 'foo\nbar'); | ||
}); | ||
|
||
test('$ can concatenate at the start of tokens followed by an array', async t => { | ||
const {stdout} = await $`echo.js ${['foo', 'bar']}foo`; | ||
t.is(stdout, 'foo\nbarfoo'); | ||
}); | ||
Comment on lines
+204
to
+207
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tested locally, but this might be another good one to include: test('$ can concatenate at the start and end of tokens surrounding an array', async t => {
const {stdout} = await $`echo.js foo${['bar', 'baz']}foo`;
t.is(stdout, 'foobar\nbazfoo');
}); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea! Added in 366e6fc |
||
|
||
test('$ can concatenate at the start and end of tokens followed by an array', async t => { | ||
const {stdout} = await $`echo.js foo${['bar', 'foo']}bar`; | ||
t.is(stdout, 'foobar\nfoobar'); | ||
}); | ||
|
||
test('$ can concatenate multiple tokens', async t => { | ||
const {stdout} = await $`echo.js ${'foo'}bar${'foo'}`; | ||
t.is(stdout, 'foobarfoo'); | ||
}); | ||
|
||
test('$ allows escaping spaces in commands with interpolation', async t => { | ||
const {stdout} = await $`${'command with space.js'} foo bar`; | ||
t.is(stdout, 'foo\nbar'); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think I still prefer the immutable composition and readability of your
parseTempates
/concatTokens
update @ehmicky, but was curious what it would look like to push and pop from a singletokens
array:Im guessing the perf difference would be negligible, but thought I’d share the exploration!