npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

oparser

v3.1.1

Published

A very forgiving key-value option parser

Readme

oparser

npm version license

A very forgiving key-value option parser for turning loose strings into usable JavaScript objects.

oparser is useful when input looks like CLI flags, JSX props, .env snippets, CMS config, or hand-written key/value text, but is not strict JSON.

npm install oparser
const { parse } = require('oparser')

parse(`
  width={999}
  enabled=TRUE
  title="Hello world"
  tags=[one, "two, too", "three]still text"]
  style={{ color: 'red', label: "b{c}" }}
`)

// {
//   width: 999,
//   enabled: true,
//   title: 'Hello world',
//   tags: ['one', 'two, too', 'three]still text'],
//   style: { color: 'red', label: 'b{c}' }
// }

Why Use It?

| Input problem | What oparser does | | --- | --- | | Mixed separators and whitespace | Accepts key=value, key = value, multiline input, and comma-separated entries | | Bare booleans | Parses enabled, enabled=true, enabled=TRUE, enabled=False | | Loose arrays | Parses [one, two, 3, true] and preserves quoted commas/brackets | | Loose objects | Parses { a: b }, {{ a: "b" }}, nested objects, and trailing commas | | Comments in config text | Removes //, /* ... */, and # comments outside quoted values | | JSX-ish values | Keeps JSX fragments and arrow functions as strings | | Large JSON values | Fast-parses raw JSON and key = <large json> without using the forgiving scanner | | Unicode keys | Accepts emoji, accented characters, and CJK as bare keys |

API

const { parse, parseValue, options } = require('oparser')

| Function | Use | | --- | --- | | parse(input) | Parse a full key/value string into an object | | parseValue(value) | Parse one freeform value into a scalar, array, or object | | options\...`| Template tag wrapper aroundparse()` |

parse(input)

parse('name=bob active=true count=3')
// { name: 'bob', active: true, count: 3 }

Empty, null, undefined, or any non-string input returns {}.

parse('')
parse(null)
parse(undefined)
parse(123)
parse({})
parse([])
// {}

parseValue(value)

parseValue('[one, two, 3]')
// ['one', 'two', 3]

parseValue('{ a: b, enabled: true }')
// { a: 'b', enabled: true }

Non-string values pass through unchanged.

parseValue(null)       // null
parseValue(123)        // 123
parseValue({ a: 1 })   // { a: 1 }

options template tag

const { options } = require('oparser')

const config = options`
  foo=bar
  enabled
`

// { foo: 'bar', enabled: true }

Object and array substitutions are encoded so embedded quote characters round-trip cleanly.

options`name=${'David Wells'} config=${{ s: 'a"b' }}`
// { name: 'David Wells', config: { s: 'a"b' } }

Parsing Behavior

Keys

Bare keys accept ASCII letters, digits, _, @, $, and any non-ASCII character above U+00A0 (Latin-1 supplement, CJK, emoji, etc.). After the first character, anything that is not whitespace, =, or a structural character is kept.

parse(`name=bob`)            // { name: 'bob' }
parse(`data-id=42`)          // { 'data-id': 42 }
parse(`$HOME=/tmp`)          // { '$HOME': '/tmp' }
parse(`@scope/pkg=1.0.0`)    // { '@scope/pkg': '1.0.0' }
parse(`café=hot`)            // { café: 'hot' }
parse(`日本=val`)            // { '日本': 'val' }
parse(`🚀=launch`)           // { '🚀': 'launch' }

Quoted keys preserve spaces and characters that would otherwise terminate a bare key.

parse(`"display name"="David Wells"`)
// { 'display name': 'David Wells' }

parse(`"a=b"=1`)
// { 'a=b': 1 }

Strings

parse(`name=bob`)
parse(`name='bob'`)
parse(`name="bob"`)
parse(`name={bob}`)
// { name: 'bob' }

Quoted values stay strings even when they look like another type.

parse(`count="123" enabled="TRUE"`)
// { count: '123', enabled: 'TRUE' }

Booleans

Bare keys become true.

parse(`isLoading disabled=false`)
// { isLoading: true, disabled: false }

Unquoted true and false are case-insensitive.

parse(`a=true b=TRUE c=True d=false e=FALSE f=False`)
// { a: true, b: true, c: true, d: false, e: false, f: false }

Quoted booleans remain strings.

parse(`a="TRUE" b='False'`)
// { a: 'TRUE', b: 'False' }

Numbers

parse(`width=999 ratio=0.25 offset=-20 sci=1.5e-3 hex=0xFF`)
// { width: 999, ratio: 0.25, offset: -20, sci: 0.0015, hex: 255 }

NaN stays a string. Infinity and -Infinity parse as numbers.

Arrays

parse(`key=[ 1, 2, 3 ]`)
// { key: [1, 2, 3] }

parse(`key=[ one, two, "three", true ]`)
// { key: ['one', 'two', 'three', true] }

Quoted commas and brackets are preserved.

parse(`key=["one,two", "a]b", "a[b", three]`)
// { key: ['one,two', 'a]b', 'a[b', 'three'] }

Sparse array slots are represented as empty strings.

parse(`key=[1,,3]`)
// { key: [1, '', 3] }

Objects

parse(`key={{ "a": "b" }}`)
parse(`key={{ "a": b }}`)
parse(`key={{ a: "b" }}`)
parse(`key={{ a: b }}`)
parse(`key={ a : b }`)
// { key: { a: 'b' } }

Quoted curly braces inside object strings are preserved.

parse(`key={{ a: "b{c}", d: "e}f" }}`)
// { key: { a: 'b{c}', d: 'e}f' } }

Nested objects work in single-line and multiline values.

parse(`
  foo={{
    baz: {
      bar: {
        fuzz: "hello"
      }
    }
  }}
`)

// { foo: { baz: { bar: { fuzz: 'hello' } } } }

Comments

Line, block, and hash comments are removed outside quoted values.

parse(`
  width=100
  // ignored
  height=200 # ignored
  label="keep # and // inside quotes"
  /*
    ignored
  */
`)

// { width: 100, height: 200, label: 'keep # and // inside quotes' }

URLs And Special Characters

Unquoted URLs are supported, including query strings, fragments, ports, IPv6, commas, braces, and brackets.

parse(`url=https://example.com?ids[]=1&ids[]=2#section`)
// { url: 'https://example.com?ids[]=1&ids[]=2#section' }

parse(`src=https://user-images.github{user}content.com/image.jpg`)
// { src: 'https://user-images.github{user}content.com/image.jpg' }

JSX-ish Values And Functions

Values wrapped in JSX-style braces are kept as strings when they are not object literals.

parse(`elem={<Component type="text" />}`)
// { elem: '<Component type="text" />' }

parse(`onClick={() => console.log('hi')}`)
// { onClick: "() => console.log('hi')" }

Large JSON

For input over 20,000 characters, oparser avoids the expensive forgiving parser when the value is valid JSON.

parse(largeJsonString)
// JSON.parse(largeJsonString)

parse(`
planets = ${largeJsonString}
`)
// { planets: JSON.parse(largeJsonString) }

If a very large input is not raw JSON and is not a single key = <json> value, parse() throws a helpful error instead of attempting the forgiving regex path.

Stringify

oparser also includes a small stringify helper for round-tripping plain objects through option strings.

const { stringify } = require('oparser/src/stringify')
const { parse } = require('oparser')

const input = {
  text: 'hello',
  enabled: true,
  tags: ['one', 'two'],
  style: { color: 'red' }
}

const str = stringify(input, { separator: ' ' })
// text="hello" enabled=true tags={["one", "two"]} style={{ color: "red" }}

parse(str)
// same shape as input

Quoted strings get a quote character that does not appear inside the value, so embedded quotes round-trip without escapes.

stringify({ msg: 'a"b' }, { separator: ' ' })  // msg='a"b'
stringify({ msg: "a'b" }, { separator: ' ' })  // msg="a'b"

Non-object inputs (null, undefined, strings, numbers, booleans) return ''. null, undefined, function values, and empty arrays/objects are filtered from output.

Design Philosophy

| Principle | Meaning | | --- | --- | | Forgiving first | Prefer useful parsing for human-written config over strict grammar errors | | Preserve quoted intent | If a value is quoted, keep it as a string | | Parse obvious types | Unquoted booleans, numbers, arrays, objects, and null become native values | | Unicode-friendly keys | Latin, CJK, and emoji are valid bare key characters | | Skip the loose parser when possible | Plain strings and large raw JSON bypass the forgiving parser path | | Keep weird-but-common JSX cases working | React-style object props, elements, and handlers are supported as practical input |

Comparison

| Tool | Best for | Difference | | --- | --- | --- | | JSON.parse | Strict JSON | Fast and standard, but rejects loose keys, comments, single quotes, bare strings, and JSX-ish values | | URLSearchParams | Query strings | Great for a=1&b=2, not for nested objects, arrays, multiline text, or comments | | minimist / CLI parsers | Command-line argv arrays | Parses shell arguments, not arbitrary multiline key/value text | | oparser | Loose option/config strings | Accepts mixed syntax and returns JavaScript objects |

Architecture

input string
    |
    |-- trim and unwrap outer quotes
    |
    |-- large JSON fast path
    |      |-- raw JSON: JSON.parse(input)
    |      `-- key = JSON: { key: JSON.parse(value) }
    |
    |-- protect quoted whitespace/comments/conflicting delimiters
    |
    |-- scan characters into key/value buffers
    |
    |-- preFormat(value)
    |
    `-- convert(value)
           |-- booleans, numbers, null
           |-- JSON.parse
           |-- json-alexander for loose JSON
           `-- quote-aware array/object fallbacks

Limitations

  • This package intentionally uses regular expressions. Be careful with untrusted server-side input and consider length limits. See OWASP's ReDoS overview: https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
  • It is not a formal grammar or JavaScript parser. It handles practical JSX-ish strings, but it does not fully parse JavaScript.
  • Duplicate keys use last-write-wins behavior.
  • Colon syntax is object-only. Top-level a: 1 is not treated as a=1.
  • For very large input, only raw JSON and single key = <json> payloads use the fast path.

Troubleshooting

| Symptom | Fix | | --- | --- | | A value becomes a boolean or number | Quote it: value="TRUE" or value="123" | | A large input throws | Use raw JSON or a single key = <json> wrapper | | Top-level a: 1 becomes odd keys | Use a=1; colon syntax is for object values | | Comments disappear | Quote the content if #, //, or /* */ should be preserved | | Duplicate keys are missing | The last value wins: a=1 a=2 becomes { a: 2 } |

FAQ

Does this parse strict JSON?

Yes. Raw JSON uses JSON.parse when possible.

Does this parse loose JSON?

Yes, for common object and array shapes like { a: b }, [one, two], single quotes, and trailing commas.

Are booleans case-sensitive?

No for unquoted values. true, TRUE, True, false, FALSE, and False parse as booleans. Quoted versions remain strings.

Can I parse one value instead of a full object?

Use parseValue(value).

parseValue('{ a: b }')
// { a: 'b' }

Are non-ASCII keys supported?

Yes. Emoji, accented characters, and CJK characters all work as bare keys. See the Keys section above.

What if a string value contains every quote type?

Stringify picks ", ', or ` based on which is absent from the value. If a value contains all three, the chosen quote is backslash-escaped on the way out, but the forgiving parser does not unescape on the way back, so a round-trip in that corner case is lossy. Prefer keeping at least one quote character out of your values, or wrap the value yourself.

What happens if I pass a non-string to parse?

parse returns {} for null, undefined, numbers, booleans, plain objects, arrays, and functions. Only string input is parsed.

Is this safe for untrusted server input?

Use caution. The parser is intentionally regex-heavy and forgiving. Put size limits around untrusted input.

License

MIT