@ashnazg/literalizer

creates literals for code-gen of JS use cases

Usage no npm install needed!

<script type="module">
  import ashnazgLiteralizer from 'https://cdn.skypack.dev/@ashnazg/literalizer';
</script>

README


title: "@ashnazg/literalizer" sidebar_label: "literalizer"

Much like the carpal-tunnel silly oneliners in utils, this liblet was born because I like the literal notation in javascript more than the simpler rules of JSON.

There's plenty of relaxed JSON parsers, but no serializers to those variants that I could find, and I want this for code-generation and config-writing templating tools.

(In other words, I want to dynamically create plain-old-javascript things and then export them as a config file like ".eslintrc.js" in the same style that would result if I did it by hand.)

Usage

At it's core, it's just an equivalent to JSON.stringify($, null, '\t') that avoids quoting keys:

var literalizer = require('@ashnazg/literalizer'); // ~/projects/ashnazg-npm/literalizer/literalizer.js

var map = {foo: 'bar'};
console.log(literalizer(map)); // => {foo: "bar"}

It doesn't (yet) take a replacer, but it does take an optional 2nd param that's a config:

var example_predefined_js_variable = ...;
var map = {foo: 'bar'};
literalizer(map, {
    name: 'variable_name_in_output',
    exports: !!caller_wants_a_node_module_file,
    fold_width: 80, // simple non-scalars who's final line is shorter than this are compacted
    local_scope: {example_predefined_js_variable} // the final output will NOT express the values of any variable mentioned here.
});

Compacting some fields based on final width

The compactor will pretty-print a map or list in one-line form if and only if:

  1. fold_width is set
  2. The final line is shorter than fold_width (including unpacked tabs at 4-space, key names, and the comma at the end.)
  3. no child element is a non-empty map or list
var literalizer = require('./literalizer.js');
var this_folds = {
    smartcode: false,
    'much foldy': 'so much fold.',
};
var this_does_not = {
    smartcode: false,
    'much foldy': 'too much to fold.',
    ribbit: 'ribbit ribbit ribbit.', // because this line pushes it above the width limit given below
};

var these_are_all_fine = ['but this is fine', 42, [], {}, null, undefined];

var but_this_array_has_a_nontrivial_child = [{f:1}];

console.log(literalizer({
    this_folds,
    this_does_not,
    these_are_all_fine,
    but_this_array_has_a_nontrivial_child
}, {
    fold_width: 80
}));

results

{
    this_folds: {smartcode: false, "much foldy": "so much fold."},
    this_does_not: {
        smartcode: false,
        "much foldy": "too much to fold.",
        ribbit: "ribbit ribbit ribbit."
    },
    these_are_all_fine: ["but this is fine", 42, [], {}, null, undefined],
    but_this_array_has_a_nontrivial_child: [
        {f: 1}
    ]
}

instant module pattern

There's some trivial syntactic sugar to wrap the final output in one of a few common file patterns:

var literalizer = require('./literalizer.js');
function genConf() {
    return {
        prop1: 'val1',
        subsettings: {
            thing:'1'
        }
    };
}
console.log(literalizer(genConf(), {exports: true}));

results

module.exports = {
    prop1: "val1",
    subsettings: {
        thing: "1"
    }
};

named module level vars

console.log(literalizer(genConf(), {name: 'new_conf'}));

results

var new_conf = {
    prop1: "val1",
    subsettings: {
        thing: "1"
    }
};

named exports

console.log(literalizer(genConf(), {name: 'subconfig', exports: true}));

results

exports.subconfig = {
    prop1: "val1",
    subsettings: {
        thing: "1"
    }
};

depend on existing local scope vars

If you're already writing js vars in the file's preamble before the part generated using this library, you can have the output re-use those instead of blindly serializing the value redundantly:

var existing_map = {foo:'bar'};
function existingFunc() {};

console.log(`
var existing_map = {foo:'bar'};
function existingFunc() {};
`);

console.log(literalizer({
    top: 'level prop',
    works_in_maps: {
        mixed: 'data',
        existingFunc,
        existing_map
    },
    works_in_lists: [
        "because we look up these by value, we can tell when to just use the var name in list mode",
        existingFunc,
        existing_map
    ]
}, {
    name: 'external',
    exports: true,
    local_scope: { existing_map, existingFunc }
}));

results

var existing_map = {foo:'bar'};
function existingFunc() {};

exports.external = {
    top: "level prop",
    works_in_maps: {
        mixed: "data",
        existingFunc,
        existing_map
    },
    works_in_lists: [
        "because we look up these by value, we can tell when to just use the var name in list mode",
        existingFunc,
        existing_map
    ]
};

TODO

  1. support renaming in local_scope.
    this_doesnt_work: {
        newname: existingFunc
    },
    
  2. regexs
  3. generators
  4. inline fat arrows

Release 0.0.1

Happy with it for the plain old json-like work I'm on right now; annoyed at how much work it takes to reflect details about a JS-function like "are you a fat arrow?" that you'd need to isomorphically print the source code in all contexts.

(Since I don't need function literals in my current projects, I'm stopping here, as playing with functions has consumed 90% of the time put in so far for imperfect results.)