Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,30 @@ console.log(result.stdout);

## API

### `tokenizeArgs(command: string): string[]`
### `tokenizeArgs(command: string, options: Options): string[]`

Parses a shell command string into an array of arguments. Properly handles:

- Quoted strings (e.g., `'"./path/to/file"'`).
- Escaped characters (e.g., `\"`).
- Multiline commands (e.g., lines ending with `\\`).

### Options

- `loose`: If `true`, the tokenizer will not throw an error when closing quotes are missing. Default is `false`.

#### Examples

```js
// Without loose option (default behavior)
// This will throw an error due to the missing closing quote
tokenizeArgs('command "arg1 arg2');

// With loose option enabled
const args = tokenizeArgs('command "arg1 arg2', { loose: true });
// ['command', 'arg1 arg2']
```

## License

This project is licensed under the [MIT License](./LICENSE).
Expand Down
15 changes: 14 additions & 1 deletion src/args-tokenizer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ test("inconsistently quoted arguments", () => {
expect(tokenizeArgs(`command "arg"um"en"t`)).toEqual(["command", "argument"]);
});

test("detects incomplete quotes ", () => {
expect(() => {
tokenizeArgs(`command "arg1 "arg2" "arg3"`);
}).toThrow("Unexpected end of string. Closing quote is missing.");
});

test("forgive incomplete quotes in loose mode", () => {
expect(tokenizeArgs(`command "arg1 "arg2" "arg3"`, { loose: true })).toEqual([
"command",
"arg1 arg2 arg3",
]);
});

test("escape quotes and spaces with other quotes", () => {
expect(tokenizeArgs(`command 'quote "' "quote '"`)).toEqual([
"command",
Expand All @@ -37,7 +50,7 @@ test("escape quotes with backslashes", () => {
});

test("escape spaces with backslashes", () => {
expect(tokenizeArgs(`command space\\ "`)).toEqual(["command", "space "]);
expect(tokenizeArgs(`command space\\ `)).toEqual(["command", "space "]);
});

test("ignore escaped newlines outside of quotes", () => {
Expand Down
15 changes: 14 additions & 1 deletion src/args-tokenizer.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
const spaceRegex = /\s/;

type Options = {
loose?: boolean;
};

/**
* Tokenize a shell string into argv array
*/
export const tokenizeArgs = (argsString: string): string[] => {
export const tokenizeArgs = (
argsString: string,
options?: Options,
): string[] => {
const tokens = [];
let currentToken = "";
let openningQuote: undefined | string;
Expand Down Expand Up @@ -50,5 +57,11 @@ export const tokenizeArgs = (argsString: string): string[] => {
if (currentToken.length > 0) {
tokens.push(currentToken);
}
if (options?.loose) {
return tokens;
}
if (openningQuote) {
throw Error("Unexpected end of string. Closing quote is missing.");
}
return tokens;
};