Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
@humanwhocodes/momoa
Advanced tools
@humanwhocodes/momoa is a JavaScript library for parsing and manipulating JSON Abstract Syntax Trees (ASTs). It provides a way to work with JSON data structures at a lower level, allowing for more advanced operations such as transformations, validations, and custom traversals.
Parsing JSON into AST
This feature allows you to parse a JSON string into an Abstract Syntax Tree (AST). The AST can then be used for further analysis or transformation.
const { parse } = require('@humanwhocodes/momoa');
const json = '{"key": "value"}';
const ast = parse(json);
console.log(ast);
Traversing the AST
This feature allows you to traverse the AST. You can define enter and leave methods to perform actions when entering or leaving nodes in the AST.
const { traverse } = require('@humanwhocodes/momoa');
const ast = parse('{"key": "value"}');
traverse(ast, {
enter(node) {
console.log(node.type);
}
});
Transforming the AST
This feature allows you to transform the AST. In this example, the value of a string node is changed, and the modified AST is then converted back into a JSON string.
const { parse, generate, traverse } = require('@humanwhocodes/momoa');
let ast = parse('{"key": "value"}');
traverse(ast, {
enter(node) {
if (node.type === 'String') {
node.value = 'newValue';
}
}
});
const newJson = generate(ast);
console.log(newJson);
Esprima is a high-performance, standard-compliant ECMAScript parser. It is used for parsing JavaScript code into an AST. While it is more focused on JavaScript, it can be used for similar purposes as @humanwhocodes/momoa when dealing with JSON-like structures.
Acorn is a small, fast, JavaScript-based parser that generates an AST. It is similar to Esprima but is known for its performance and modularity. Like @humanwhocodes/momoa, it can be used to parse and manipulate code structures, though it is more general-purpose.
json-to-ast is a library specifically designed to parse JSON into an AST. It is similar to @humanwhocodes/momoa in its focus on JSON, but it is more lightweight and may lack some of the advanced traversal and transformation features.
If you find this useful, please consider supporting my work with a donation.
Momoa is a general purpose JSON utility toolkit, containing:
JavaScript defines the JSON
object with methods for both parsing strings into objects and converting objects into JSON-formatted strings. In most cases, this is exactly what you need and should use without question. However, these methods aren't useful for more fine-grained analysis of JSON structures. For instance, you'll never know if a JSON object contains two properties with the same names because JSON.parse()
will ignore the first one and return the value of the second. A tool like Momoa comes in handy when you want to know not just the result of JSON parsing, but exactly what is contained in the original JSON string.
npm install @humanwhocodes/momoa
# or
yarn add @humanwhocodes/momoa
Import into your Node.js project:
// CommonJS
const { parse } = require("@humanwhocodes/momoa");
// ESM
import { parse } from "@humanwhocodes/momoa";
Import into your Deno project:
import { parse } from "https://cdn.skypack.dev/@humanwhocodes/momoa?dts";
Install using this command:
bun add @humanwhocodes/momoa
Import into your Bun project:
import { parse } from "@humanwhocodes/momoa";
It's recommended to import the minified version to save bandwidth:
import { parse } from "https://cdn.skypack.dev/@humanwhocodes/momoa?min";
However, you can also import the unminified version for debugging purposes:
import { parse } from "https://cdn.skypack.dev/@humanwhocodes/momoa";
To parse a JSON string into an AST, use the parse()
function:
const { parse } = require("@humanwhocodes/momoa");
const ast = parse(some_json_string);
The parse()
function accepts a second argument, which is an options object that may contain one or more of the following properties:
mode
(default: "json"
) - specify the parsing mode. Possible options are "json"
, "jsonc"
(JSON with comments), and "json5"
.ranges
(default: false
) - set to true
if you want each node to also have a range
property, which is an array containing the start and stop index for the syntax within the source string.tokens
- set to true
to return a tokens
property on the root node containing all of the tokens used to parse the code. If mode
is "jsonc"
or "json5"
, then the tokens include comment tokens.allowTrailingCommas
- set to true
to allow trailing commas in arrays and objects in "json"
and "jsonc"
modes. This option is ignored in JSON5 mode.Here's an example of passing options:
const { parse } = require("@humanwhocodes/momoa");
const ast = parse(some_json_string, {
mode: "jsonc",
ranges: true,
tokens: true
});
// root now has a range array
console.dir(ast.range);
// root now has a tokens array
console.dir(ast.tokens);
To produce JSON tokens from a string, use the tokenize()
function:
const { tokenize } = require("@humanwhocodes/momoa");
const json = "{\"foo\":\"bar\"}";
for (const token of tokenize(json)) {
console.log("Token type is", token.type);
const start = token.loc.start.offset;
const end = token.loc.end.offset;
console.log("Token value is", json.slice(start, end));
}
The tokenize()
function accepts a second parameter, which is an options object that may contain one or more of the following properties:
mode
(default: "json"
) - specify the parsing mode. Possible options are "json"
, "jsonc"
(JSON with comments), and "json5"
.ranges
(default: false
) - set to true
if you want each token to also have a range
property, which is an array containing the start and stop index for the syntax within the source string.There are two ways to traverse an AST: iteration and traditional traversal.
Iteration uses a generator function to create an iterator over the AST:
const { parse, iterator } = require("@humanwhocodes/momoa");
const ast = parse(some_json_string);
for (const { node, parent, phase } of iterator(ast)) {
console.log(node.type);
console.log(phase); // "enter" or "exit"
}
Each step of the iterator returns an object with three properties:
node
- the node that the traversal is currently visitingparent
- the parent node of node
phase
- a string indicating the phase of traversal ("enter"
when first visiting the node, "exit"
when leaving the node)You can also filter the iterator by passing in a filter function. For instance, if you only want steps to be returned in the "enter"
phase, you can do this:
const { parse, iterator } = require("@humanwhocodes/momoa");
const ast = parse(some_json_string);
for (const { node } of iterator(ast, ({ phase }) => phase === "enter")) {
console.log(node.type);
}
Traversing uses a function that accepts an object with enter
and exit
properties:
const { parse, traverse } = require("@humanwhocodes/momoa");
const ast = parse(some_json_string);
traverse(ast, {
enter(node, parent) {
console.log("Entering", node.type);
},
exit(node, parent) {
console.log("Exiting", node.type);
}
});
To convert an AST into the JavaScript value it represents, use the evaluate()
function:
const { parse, evaluate } = require("@humanwhocodes/momoa");
// same as JSON.parse(some_json_string)
const ast = parse(some_json_string);
const value = evaluate(ast);
In this example, value
is the same result you would get from calling JSON.parse(some_json_string)
(ast
is the intermediate format representing the syntax).
To convert an AST back into a JSON string, use the print()
function:
const { parse, print } = require("@humanwhocodes/momoa");
const ast = parse(some_json_string);
const text = print(ast);
Note: The printed AST will not produce the same result as the original JSON text as the AST does not preserve whitespace.
You can modify the output of the print()
function by passing in an object with an indent
option specifying the number of spaces to use for indentation. When the indent
option is passed, the text produced will automatically have newlines insert after each {
, }
, [
, ]
, and ,
characters.
const { parse, print } = require("@humanwhocodes/momoa");
const ast = parse(some_json_string);
const text = print(ast, { indent: 4 });
Momoa also exports a map of traversable properties in AST nodes that is helpful if you'd like to traverse an AST manually. This is a map where the keys are the type
property of each AST node and the values are an array of property names to traverse.
const { visitorKeys } = require("@humanwhocodes/momoa");
console.log(visitorKeys.get("Document")); // "body"
To work on Momoa, you'll need:
Make sure both are installed by visiting the links and following the instructions to install.
Now you're ready to clone the repository:
git clone https://github.com/humanwhocodes/momoa.git
Then, enter the directory and install the dependencies:
cd momoa/js
npm install
After that, you can run the tests via:
npm test
Note: Momoa builds itself into a single file for deployment. The npm test
command automatically rebuilds Momoa into that single file whenever it is run. If you are testing in a different way, then you may need to manually rebuild using the npm run build
command.
This project takes inspiration (but not code) from a number of other projects:
Esprima
inspired the package interface and AST format.json-to-ast
inspired the AST format.parseJson.js
inspired me by showing writing a parser isn't all that hard.Apache 2.0
Momoa is the last name of American actor Jason Momoa. Because "JSON" is pronounced "Jason", I wanted a name that played off of this fact. The most obvious choice would have been something related to Jason and the Argonauts, as this movie is referenced in the JSON specification directly. However, both "Argo" and "Argonaut" were already used for open source projects. When I did a search for "Jason" online, Jason Momoa was the first result that came up. He always plays badass characters so it seemed to fit.
There are a number of programs that allow C-style comments in JSON files, most notably, configuration files for Visual Studio Code. As there seems to be a need for this functionality, I decided to add it out-of-the-box.
FAQs
JSON AST parser, tokenizer, printer, traverser.
The npm package @humanwhocodes/momoa receives a total of 451,550 weekly downloads. As such, @humanwhocodes/momoa popularity was classified as popular.
We found that @humanwhocodes/momoa demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.