string-array parses a bracketed, comma separated string such as "[a, b, [c]]" into a nested array of strings. It has no runtime dependencies. ESM only ("type": "module").
import { parse } from "string-array";
import type { ParseResult, StringArray } from "string-array";Everything is a named export from the package root. There is no default export and there are no subpaths (only string-array/package.json). The exports map has a single default condition pointing at an ESM file.
Runtime exports: parse.
Type-only exports: StringArray, ParseResult.
type StringArray = string | StringArray[];An element is either a string or a nested array of elements. Leaves are always strings. Quote characters are never interpreted, and no numbers or booleans are produced.
interface ParseResult {
prefix: string;
array: StringArray[];
remain: string;
}| Field | Type | Behavior |
|---|---|---|
prefix |
string |
Trimmed text before the first [. "" when there is none, when noPrefix is true, or when the input is empty. |
array |
StringArray[] |
The outermost parsed array. Its items are strings or nested arrays. [] for empty input. |
remain |
string |
Text left after the first complete top-level array, with leading whitespace and one separating , removed. The input is trimmed up front, so remain has no outer whitespace. "" when nothing is left. |
function parse(str: string, noPrefix?: boolean, noExtra?: boolean): ParseResultParses one array from the start of str (after an optional prefix) and returns it with the prefix and the unparsed rest.
| Parameter | Type | Default | Behavior |
|---|---|---|---|
str |
string |
none | Input text. Trimmed first. A non-string throws TypeError. |
noPrefix |
boolean |
undefined |
Only the exact value true disables prefix detection. Any other value, including truthy ones such as 1, keeps it enabled. |
noExtra |
boolean |
undefined |
Only the exact value true makes text after the top-level array an error. Any other value allows it and returns it in remain. |
Informal, as implemented:
input := ws* [ prefix ] array rest
prefix := text before the first "[", trimmed; only when that "[" is not at index 0
array := "[" ws* items "]"
items := (element | array) separated by ","
element := any characters up to the next "," or "]", trimmed
rest := ws* [ "," ws* ] remain
- Brackets:
[opens an array and]closes it. Consecutive openers such as[[[a]]]nest. - Nesting: unbounded depth. The parser is iterative, with no recursion.
- Separator:
,. Whitespace around elements, brackets and separators is trimmed (String.prototype.trimsemantics). - Elements: raw text, trimmed. Quotes (
',", backtick) stay part of the element. There is no escape character and no way to put,or]inside an element. A[in the middle of an element is accepted as plain text (see edge cases). - The comma after a nested array is optional. If present directly after its
], it is consumed. - Only the first top-level array is parsed. Later text is not parsed, it is returned in
remain.
stris trimmed. If the result is empty, return{ prefix: "", array: [], remain: "" }. This happens before any other check, sonoPrefixandnoExtrado not matter for empty input.- Unless
noPrefix === true, find the first[. If its index is greater than 0,prefixis the text before it (trimmed) and parsing starts at that[. If the index is 0 or there is no[,prefixis""and the text is parsed as is. - The parse loop: if the remaining text starts with
[, a new array opens (leading whitespace after it is skipped). Otherwise, if no array is open, it throwsarray missing [. - In an open array, the next element runs up to the next
,or]. If neither exists it throwsarray missing ]. - The element is trimmed. A non-empty element is pushed. An empty element is pushed as
""only when it ends at,. An empty element ending at]is dropped. - On
], the array closes. A single,that follows is consumed. - When the outermost array closes, the loop stops. If text remains and
noExtra === true, it throwsextra data at end of array. Otherwise the text is returned asremain.
array is the outermost array itself. "[a, [b]]" gives array: ["a", ["b"]]. Each call returns fresh objects.
| Input | Result |
|---|---|
"" or whitespace only |
{ prefix: "", array: [], remain: "" }. No error, with any flags. |
No [ ("abc") |
Throws array missing [. |
Text then [, with noPrefix: true ("pre[a]") |
Throws array missing [. |
"]" |
Throws array missing [. |
"[a", "[[a]" |
Throws array missing ]. |
null, undefined, number, array |
Throws TypeError from str.trim. Not guarded. |
All thrown errors are plain Error instances (not AssertionError) except the TypeError for non-string input.
| Message | When |
|---|---|
array missing [ |
Input is non-empty and the parser needs an opening [ but finds none: no [ in the input, text before [ with noPrefix: true, or input starting with ]. |
array missing ] |
An array is still open and there is no further , or ], or the input ends while an array is open. |
extra data at end of array |
The top-level array closed, text remains, and noExtra === true. |
An unmatched ] after a complete array is not an error. It lands in remain, or throws extra data at end of array with noExtra.
| Input | Result |
|---|---|
"[]", "[ ]" |
array: [] |
"[a,]" |
["a"]. A trailing comma adds nothing. |
"[,]" |
[""] |
"[a,,b]" |
["a", "", "b"] |
"[a,,]" |
["a", ""] |
"[[a],]" |
[["a"]] |
"[[a] b]" |
[["a"], "b"]. The comma after a nested array is optional. |
"[[],[]]", "[a,[]]" |
[[], []], ["a", []] |
"[ 'x,y' ]" |
["'x", "y'"]. Quotes do not protect the comma. |
"[a [b]]" |
array: ["a [b"], remain: "]". The inner [ is plain text, so the first ] closes the outer array. |
"pre [a] post" |
prefix: "pre", array: ["a"], remain: "post" |
"x]y[a]" |
prefix: "x]y", array: ["a"] |
"[a], x" |
remain: "x". The comma after ] is consumed. |
"[a] x [b]" |
remain: "x [b]". The second array is not parsed. |
"[a]]" |
remain: "]" |
"[a][" |
remain: "[" |
"[a],", "[a] , " |
remain: "", even with noExtra: true. The comma is consumed and nothing is left. |
"[a] ,x" with noExtra |
Throws extra data at end of array. |
"pre[a]" with noExtra |
Fine. A prefix is not extra data. |
parse("tag[ hello, world, [ 1, [2] ] ] rest");
// { prefix: "tag", array: ["hello", "world", ["1", ["2"]]], remain: "rest" }
parse("[a, b] more", false, true); // throws Error("extra data at end of array")