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

isotropic-value-to-source

v0.10.0

Published

Serialize a value to a source code string

Readme

isotropic-value-to-source

npm version License

A utility that converts JavaScript values into formatted, syntactically correct source code strings.

Why Use This?

  • Code Generation: Generate properly formatted JavaScript code programmatically
  • Beyond JSON.stringify: Correctly handles many JavaScript types including ArrayBuffer, BigInt, DataView, Map, RegExp, Set, Symbol, Temporal, typed arrays, URL, URLSearchParams, and more
  • Linter-Friendly: Produces code that passes Isotropic linting rules
  • Pretty Formatting: Consistent indentation, line breaks, and property sorting
  • Configurable Output: Customize formatting, property order, quote styles, and more
  • Circular References: Safely handles circular structures
  • Error Serialization: Converts any error (built-in, subclass, isotropic-error, or custom) into a plain object capturing its message, name, stack, and nested causes
  • Unserializable Values: Marks values that cannot be represented as source (such as Promise and WeakMap) with a configurable token
  • Symbol Keys & Null Prototypes: Optionally serializes symbol-keyed properties and preserves null-prototype objects
  • Method Preservation: Option to include functions in the serialized output

Installation

npm install isotropic-value-to-source

Usage

import _valueToSource from 'isotropic-value-to-source';

{
    // Basic usage
    const code = _valueToSource({
        name: 'John',
        age: 30,
        hobbies: [
            'reading',
            'cycling'
        ],
        active: true
    });

    console.log(code);
    // Output:
    // {
    //     active: true,
    //     age: 30,
    //     hobbies: [
    //         'reading',
    //         'cycling'
    //     ],
    //     name: 'John'
    // }

    // Custom configuration
    const customCode = _valueToSource(data, {
        doubleQuote: true, // Use double quotes instead of single quotes
        includeUndefinedProperties: true, // Include undefined properties in output
        indentString: '  ' // Use 2 spaces for indentation
    });
}

API

valueToSource(value, options)

Converts a JavaScript value to a source code string.

Parameters

  • value (Any): The value to convert to a source code string
  • options (Object, optional): Configuration options object:
    • circularReferenceToken (String): Token to use for circular references. Default: 'CIRCULAR_REFERENCE'
    • doubleQuote (Boolean): Whether to use double quotes instead of single quotes. Default: false
    • includeFunctions (Boolean): Whether to include functions in the output. Default: false
    • includeSymbolKeys (Boolean): Whether to include symbol-keyed properties in the output. Only enumerable symbol-keyed properties are included. Default: false
    • includeUndefinedProperties (Boolean): Whether to include properties with undefined values. Default: false
    • indentLevel (Number): Initial indentation level. Default: 0
    • indentString (String): String to use for indentation. Default: ' ' (4 spaces)
    • lineEnding (String): String to use for line endings. Default: '\n'
    • propertySort (Object): Options for property sorting:
      • caseSensitive (Boolean): Whether to sort properties case-sensitively. Default: false
      • direction (String): Sort direction, either 'asc' or 'desc'. Default: 'asc'
      • ignoreSpecialCharacters (Boolean): Whether to ignore special characters when sorting. Default: true
      • prefixPositions (Object): Map of prefixes to their sort positions ('first' or 'last'). Default: { _: 'last' }
    • unserializableToken (String): Token to use in the output in place of a value that cannot be represented as source code (see Unserializable Values). Default: 'UNSERIALIZABLE'

Returns

  • (String): A JavaScript source code string representing the input value

Examples

Basic Types

import _valueToSource from 'isotropic-value-to-source';

// Numbers
_valueToSource(42);       // "42"
_valueToSource(Infinity); // "Infinity"
_valueToSource(NaN);      // "NaN"
_valueToSource(-0);       // "-0" (preserves negative zero)

// Strings
_valueToSource('hello');  // "'hello'"
_valueToSource("quote's"); // "'quote\\'s'"

// Booleans
_valueToSource(true);     // "true"
_valueToSource(false);    // "false"

// Undefined and null
_valueToSource(undefined); // "void null"
_valueToSource(null);      // "null"

// BigInt
_valueToSource(42n);      // "42n"

// Symbols
_valueToSource(Symbol());              // "Symbol()"
_valueToSource(Symbol('description')); // "Symbol('description')"
_valueToSource(Symbol.for('key'));     // "Symbol.for('key')"

Complex Types

import _valueToSource from 'isotropic-value-to-source';

// Arrays
_valueToSource([1, 2, 3]);
// [
//     1,
//     2,
//     3
// ]

// Nested arrays
_valueToSource([[1], [2, 3]]);
// [
//     [
//         1
//     ],
//     [
//         2,
//         3
//     ]
// ]

// Objects
_valueToSource({ a: 1, b: 2 });
// {
//     a: 1,
//     b: 2
// }

// Nested objects
_valueToSource({ a: { b: { c: 3 } } });
// {
//     a: {
//         b: {
//             c: 3
//         }
//     }
// }

// Dates are converted to a Temporal.Instant (Date is no longer a supported output type)
_valueToSource(new Date('2023-01-01'));
// Temporal.Instant.from('2023-01-01T00:00:00.000Z')

// Regular expressions
_valueToSource(/^test$/gi);
// /^test$/gi

// Sets
_valueToSource(new Set([1, 2, 3]));
// new Set([
//     1,
//     2,
//     3
// ])

// Maps
_valueToSource(new Map([['key1', 'value1'], ['key2', 'value2']]));
// new Map([
//     [
//         'key1',
//         'value1'
//     ],
//     [
//         'key2',
//         'value2'
//     ]
// ])

// Temporal (all Temporal types are supported)
_valueToSource(Temporal.Instant.from('2023-01-01T00:00:00Z'));
// Temporal.Instant.from('2023-01-01T00:00:00Z')

_valueToSource(Temporal.ZonedDateTime.from('2023-01-01T12:30:00-05:00[America/New_York]'));
// Temporal.ZonedDateTime.from('2023-01-01T12:30:00-05:00[America/New_York]')

_valueToSource(Temporal.PlainDate.from('2023-01-01'));
// Temporal.PlainDate.from('2023-01-01')

_valueToSource(Temporal.PlainTime.from('12:30:45'));
// Temporal.PlainTime.from('12:30:45')

_valueToSource(Temporal.PlainDateTime.from('2023-01-01T12:30:45'));
// Temporal.PlainDateTime.from('2023-01-01T12:30:45')

_valueToSource(Temporal.Duration.from('P1Y2M3DT4H5M6S'));
// Temporal.Duration.from('P1Y2M3DT4H5M6S')
// (Temporal.PlainYearMonth and Temporal.PlainMonthDay are supported too)

// Typed arrays (every typed array variant is supported)
_valueToSource(new Uint8Array([1, 2, 3]));
// new Uint8Array([
//     1,
//     2,
//     3
// ])

_valueToSource(new BigInt64Array([1n, -2n]));
// new BigInt64Array([
//     1n,
//     -2n
// ])

// ArrayBuffer (reconstructed via a Uint8Array view)
_valueToSource(new Uint8Array([10, 20, 30]).buffer);
// new Uint8Array([
//     10,
//     20,
//     30
// ]).buffer

// DataView
_valueToSource(new DataView(new Uint8Array([1, 2, 3, 4]).buffer));
// new DataView(new Uint8Array([
//     1,
//     2,
//     3,
//     4
// ]).buffer)

// URL
_valueToSource(new URL('https://example.com/path'));
// new URL('https://example.com/path')

// URLSearchParams
_valueToSource(new URLSearchParams('a=1&b=2&a=3'));
// new URLSearchParams('a=1&b=2&a=3')

Working with Functions

import _valueToSource from 'isotropic-value-to-source';

{
    // Including function properties in objects
    const object = {
        farewell: function () {
            return `Goodbye ${this.name}`;
        },
        greet () {
            return `Hello ${this.name}`;
        },
        name: 'John'
    };

    _valueToSource(object, {
        includeFunctions: true
    });
    // {
    //     farewell: function () {
    //         return `Goodbye ${this.name}`;
    //     },
    //     greet () {
    //         return `Hello ${this.name}`;
    //     },
    //     name: 'John'
    // }
}

{
    // Including functions in arrays
    const array = [
        () => 'arrow function',
        function () {
            return 'function expression';
        }
    ];

    _valueToSource(array, {
        includeFunctions: true
    });
    // [
    //     () => 'arrow function',
    //     function () {
    //         return 'function expression';
    //     }
    // ]
}

When a property value is a method (shorthand, async, generator, or one whose name requires quoting or is computed), it is emitted as a method definition; other function values are emitted as function expressions or arrow functions. A few caveats apply when includeFunctions is true:

  • A function's body is reproduced from its toString() representation, so the original source's indentation and line endings are preserved rather than reformatted to match the surrounding output.
  • Property getters and setters are invoked during traversal (their returned values are serialized), not preserved as accessors.
  • A method that has been assigned to a property whose name differs from the method's own name may not preserve the property name.

Boxed Primitives

Boxed primitive wrapper objects are unwrapped to their primitive values.

import _valueToSource from 'isotropic-value-to-source';

_valueToSource(new Number(5));      // "5"
_valueToSource(new String('hi'));   // "'hi'"
_valueToSource(new Boolean(true));  // "true"

Symbol Keys

By default, symbol-keyed properties are omitted. Set includeSymbolKeys to true to include enumerable symbol-keyed properties, which are emitted as computed keys after the string-keyed properties. Non-enumerable symbol-keyed properties are always omitted.

import _valueToSource from 'isotropic-value-to-source';

{
    const object = {
        name: 'config'
    };

    object[Symbol('tag')] = 'x';
    object[Symbol.for('registered')] = 'y';

    _valueToSource(object, {
        includeSymbolKeys: true
    });
    // {
    //     name: 'config',
    //     [Symbol('tag')]: 'x',
    //     [Symbol.for('registered')]: 'y'
    // }
}

Null-Prototype Objects

Objects with a null prototype (for example, those created with Object.create(null)) are preserved as such in the output.

import _valueToSource from 'isotropic-value-to-source';

_valueToSource(Object.create(null));
// Object.create(null)

{
    const object = Object.assign(Object.create(null), {
        a: 1,
        b: 2
    });

    _valueToSource(object);
    // Object.assign(Object.create(null), {
    //     a: 1,
    //     b: 2
    // })
}

An own property named __proto__ is always emitted as a computed key (['__proto__']) so that it round-trips as a genuine own property instead of reassigning the prototype.

Errors

Any error instance (a built-in Error, a built-in subclass such as RangeError, an AggregateError, isotropic-error, or a custom error class) is serialized as a plain object capturing its properties. This includes the message, the stack trace, any own enumerable properties, and nested causes or aggregated errors (which are themselves serialized recursively). A name property is added when nothing else in the output already identifies the error type, so a RangeError remains recognizable. Cycles between errors are handled with the circularReferenceToken, just like any other circular structure.

The output is an ordinary object literal rather than a reconstructed error, so it neither imports nor depends on any particular error implementation.

import _valueToSource from 'isotropic-value-to-source';

_valueToSource(new RangeError('index out of bounds'));
// {
//     message: 'index out of bounds',
//     name: 'RangeError',
//     stack: 'RangeError: index out of bounds\n    at ...'
// }

// Nested causes are serialized recursively
_valueToSource(new Error('request failed', {
    cause: new Error('connection refused')
}));
// {
//     cause: {
//         message: 'connection refused',
//         name: 'Error',
//         stack: 'Error: connection refused\n    at ...'
//     },
//     message: 'request failed',
//     name: 'Error',
//     stack: 'Error: request failed\n    at ...'
// }

// Aggregated errors (including the errors-within-details pattern) are serialized too
_valueToSource({
    details: {
        errors: [
            new Error('first'),
            new Error('second')
        ]
    },
    message: 'multiple failures'
});
// {
//     details: {
//         errors: [
//             {
//                 message: 'first',
//                 name: 'Error',
//                 stack: '...'
//             },
//             {
//                 message: 'second',
//                 name: 'Error',
//                 stack: '...'
//             }
//         ]
//     },
//     message: 'multiple failures'
// }

Unserializable Values

Some values cannot be represented as source code that reconstructs an equivalent value. These are replaced in the output with the unserializableToken (default 'UNSERIALIZABLE'), much like circular references. The values treated as unserializable are Promise, WeakMap, WeakSet, WeakRef, and FinalizationRegistry.

import _valueToSource from 'isotropic-value-to-source';

_valueToSource({
    cache: new WeakMap(),
    ok: true,
    pending: Promise.resolve()
});
// {
//     cache: UNSERIALIZABLE,
//     ok: true,
//     pending: UNSERIALIZABLE
// }

// Custom token
_valueToSource(new WeakMap(), {
    unserializableToken: '/* unserializable */'
});
// /* unserializable */

Handling Circular References

import _valueToSource from 'isotropic-value-to-source';

{
    // Creating a circular structure
    const object1 = {
            name: 'Object 1'
        },
        object2 = {
            name: 'Object 2',
            reference: object1
        };

    object1.reference = object2;  // Creates a circular reference

    // Default handling
    _valueToSource(object1);
    // {
    //     name: 'Object 1',
    //     reference: {
    //         name: 'Object 2',
    //         reference: CIRCULAR_REFERENCE
    //     }
    // }

    // Custom circular reference token
    _valueToSource(object1, {
        circularReferenceToken: '/* circular */'
    });
    // {
    //     name: 'Object 1',
    //     reference: {
    //         name: 'Object 2',
    //         reference: /* circular */
    //     }
    // }
}

Customizing Output Format

import _valueToSource from 'isotropic-value-to-source';

{
    const data = {
        title: 'Example',
        items: [
            1,
            2,
            3
        ],
        details: {
            created: new Date('2023-01-01'),
            active: true
        }
    };

    // Change quote style and indentation
    _valueToSource(data, {
        doubleQuote: true,
        indentString: '  ',
        lineEnding: '\r\n'
    });
    // {
    //   details: {
    //     active: true,
    //     created: Temporal.Instant.from("2023-01-01T00:00:00.000Z")
    //   },
    //   items: [
    //     1,
    //     2,
    //     3
    //   ],
    //   title: "Example"
    // }

    // Custom property sorting
    _valueToSource(data, {
        propertySort: {
            caseSensitive: true, // Sort case-sensitively
            direction: 'desc', // Sort in descending order
            ignoreSpecialCharacters: false, // Don't ignore special characters
            prefixPositions: {
                title: 'first' // Put 'title' property first
            }
        }
    });
}

Code Generation

import _fs from 'node:fs/promises';
import _valueToSource from 'isotropic-value-to-source';

const _generateConfigModule = async ({
    config,
    filepath
}) => {
    console.log(`Writing configuration module to ${filepath}`);

    await _fs.writeFile(filepath, [
        '// Auto-generated configuration file',
        `// Generated on ${new Date().toISOString()}`,
        '',
        `export default ${_valueToSource(config)};`,
        ''
    ].join('\n'));

    console.log(`Configuration module written to ${filepath}`);
};

{
    // Usage
    _generateConfigModule({
        config: {
            apiEndpoint: 'https://api.example.com',
            features: {
                analytics: {
                    enabled: true,
                    trackErrors: true
                },
                logging: true
            },
            retryAttempts: 3,
            timeout: 30000,
        },
        filepath: './src/config.js'
    });
}

Differences from JSON.stringify

While JSON.stringify is great for serializing data for data interchange, valueToSource is designed for code generation:

| Feature | JSON.stringify | valueToSource | | ------------------------------------------- | ---------------- | --------------------------- | | Output format | JSON | JavaScript source code | | Handles functions | ❌ | ✅ (with option) | | Handles RegExp | ❌ | ✅ | | Handles Map, Set | ❌ | ✅ | | Handles Error | ❌ (becomes {}) | ✅ (as a plain object) | | Handles Temporal types | ❌ | ✅ | | Handles Date | ✅ (as a string) | ✅ (as a Temporal.Instant) | | Handles typed arrays, ArrayBuffer, DataView | ❌ | ✅ | | Handles URL, URLSearchParams | ❌ | ✅ | | Handles Symbol | ❌ | ✅ | | Handles symbol-keyed properties | ❌ | ✅ (with option) | | Handles BigInt | ❌ | ✅ | | Unwraps boxed primitives | ✅ | ✅ | | Preserves null-prototype objects | ❌ | ✅ | | Preserves negative zero | ❌ (becomes 0) | ✅ | | Handles undefined | ❌ | ✅ (with option) | | Circular references | ❌ (throws error) | ✅ (with token) | | Unserializable values | ❌ (omits or throws) | ✅ (with token) | | Pretty-printing | Limited | Advanced formatting options | | Property sorting | ❌ | ✅ (with multiple options) | | Quote style options | ❌ | ✅ |

Contributing

Please refer to CONTRIBUTING.md for contribution guidelines.

Issues

If you encounter any issues, please file them at https://github.com/ibi-group/isotropic-value-to-source/issues