@kaynooo/cli-kit 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/shell.ts +10 -3
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kaynooo/cli-kit",
3
3
  "type": "module",
4
- "version": "0.8.0",
4
+ "version": "0.8.1",
5
5
  "description": "What every @kaynooo CLI shares: argument parsing with --help, a command dispatcher with typo hints and shell completion, and output formatting.",
6
6
  "author": "Kaynooo <kaynooo@kevyn.fr>",
7
7
  "license": "MIT",
package/src/shell.ts CHANGED
@@ -11,6 +11,7 @@ const LEADING_TILDE = /^~(?=\/|$)/
11
11
  const LEADING_TABS = /^\t+/
12
12
  const OPENERS = /^[({]+/
13
13
  const CLOSERS = /[)}]+$/
14
+ const DOUBLE_QUOTED_ESCAPES = new Set(['$', '`', '"', '\\'])
14
15
  const HEREDOC = /^<<(-?)[ \t]*(?:'([^']*)'|"([^"]*)"|([^\s;&|<>()-][^\s;&|<>()]*))/
15
16
  // Programs that run the command following them, which is the one that counts.
16
17
  const WRAPPERS = new Set(['command', 'env', 'sudo', 'exec', 'nohup', 'time'])
@@ -276,7 +277,10 @@ export function words(text: string): Word[] {
276
277
 
277
278
  isStarted = true
278
279
 
279
- if (char === '\\' && quote !== '\'')
280
+ // within double quotes a backslash escapes only `$`, a backtick, `"` and itself, and stays before anything else (`"v\d"`)
281
+ if (char === '\\' && quote === '"' && !DOUBLE_QUOTED_ESCAPES.has(text[i + 1] ?? ''))
282
+ current += char
283
+ else if (char === '\\' && quote !== '\'')
280
284
  current += text[++i] ?? ''
281
285
  else if (quote !== null && char === quote)
282
286
  quote = null
@@ -329,8 +333,11 @@ export function parseCommand(text: string): Invocation {
329
333
  break
330
334
  }
331
335
  }
332
- // the closing of a subshell or a group, `(git stash)` or `{ cd x && npm i; }`, ends the last word
333
- const last = (tokens.at(-1) ?? '').replace(CLOSERS, '')
336
+ // the closing of a subshell or a group, `(git stash)` or `{ cd x && npm i; }`, ends the last word; a closer inside quotes (`-e "f(g(x))"`) is the word's own
337
+ const closers = CLOSERS.exec(text.trimEnd())?.[0].length ?? 0
338
+ const word = tokens.at(-1) ?? ''
339
+ const trimmed = word.length - (CLOSERS.exec(word)?.[0].length ?? 0)
340
+ const last = word.slice(0, Math.max(trimmed, word.length - closers))
334
341
 
335
342
  if (tokens.length > 0)
336
343
  tokens.splice(-1, 1, ...(last === '' ? [] : [last]))