Skip to content

Commit 27ca46d

Browse files
authored
Merge pull request #1 from TrySound/strict
Tokenize strictly by default
2 parents 1bedae9 + b3d3b64 commit 27ca46d

File tree

3 files changed

+45
-3
lines changed

3 files changed

+45
-3
lines changed

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,30 @@ console.log(result.stdout);
6262

6363
## API
6464

65-
### `tokenizeArgs(command: string): string[]`
65+
### `tokenizeArgs(command: string, options: Options): string[]`
6666

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

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

73+
### Options
74+
75+
- `loose`: If `true`, the tokenizer will not throw an error when closing quotes are missing. Default is `false`.
76+
77+
#### Examples
78+
79+
```js
80+
// Without loose option (default behavior)
81+
// This will throw an error due to the missing closing quote
82+
tokenizeArgs('command "arg1 arg2');
83+
84+
// With loose option enabled
85+
const args = tokenizeArgs('command "arg1 arg2', { loose: true });
86+
// ['command', 'arg1 arg2']
87+
```
88+
7389
## License
7490

7591
This project is licensed under the [MIT License](./LICENSE).

src/args-tokenizer.test.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@ test("inconsistently quoted arguments", () => {
2121
expect(tokenizeArgs(`command "arg"um"en"t`)).toEqual(["command", "argument"]);
2222
});
2323

24+
test("detects incomplete quotes ", () => {
25+
expect(() => {
26+
tokenizeArgs(`command "arg1 "arg2" "arg3"`);
27+
}).toThrow("Unexpected end of string. Closing quote is missing.");
28+
});
29+
30+
test("forgive incomplete quotes in loose mode", () => {
31+
expect(tokenizeArgs(`command "arg1 "arg2" "arg3"`, { loose: true })).toEqual([
32+
"command",
33+
"arg1 arg2 arg3",
34+
]);
35+
});
36+
2437
test("escape quotes and spaces with other quotes", () => {
2538
expect(tokenizeArgs(`command 'quote "' "quote '"`)).toEqual([
2639
"command",
@@ -37,7 +50,7 @@ test("escape quotes with backslashes", () => {
3750
});
3851

3952
test("escape spaces with backslashes", () => {
40-
expect(tokenizeArgs(`command space\\ "`)).toEqual(["command", "space "]);
53+
expect(tokenizeArgs(`command space\\ `)).toEqual(["command", "space "]);
4154
});
4255

4356
test("ignore escaped newlines outside of quotes", () => {

src/args-tokenizer.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
const spaceRegex = /\s/;
22

3+
type Options = {
4+
loose?: boolean;
5+
};
6+
37
/**
48
* Tokenize a shell string into argv array
59
*/
6-
export const tokenizeArgs = (argsString: string): string[] => {
10+
export const tokenizeArgs = (
11+
argsString: string,
12+
options?: Options,
13+
): string[] => {
714
const tokens = [];
815
let currentToken = "";
916
let openningQuote: undefined | string;
@@ -50,5 +57,11 @@ export const tokenizeArgs = (argsString: string): string[] => {
5057
if (currentToken.length > 0) {
5158
tokens.push(currentToken);
5259
}
60+
if (options?.loose) {
61+
return tokens;
62+
}
63+
if (openningQuote) {
64+
throw Error("Unexpected end of string. Closing quote is missing.");
65+
}
5366
return tokens;
5467
};

0 commit comments

Comments
 (0)