/shst

Primary LanguageTypeScriptMIT LicenseMIT

shst

Version Build Status Coverage Dependencies Vulnerabilities License Types



shst

A shell parser for Javascript

Usage

Once built, transpiled JS files can be found at /package/pkg. They expose the Parser, InteractiveParser, and Printer classes, and the traverse() function.

Full documentation is available here.

Examples

Parsing

Script parsing

const { Parser, Printer } = require('./package/pkg');

const parser = new Parser();
const tree = parser.parse("echo 'foo'");

const printer = new Printer();
console.log(printer.print(tree)); // echo 'foo'

Interactive parsing

const { InteractiveParser } = require('./package/pkg');

const interactive = new InteractiveParser();

let acc = [];
interactive.subscribe((stmts) => (acc = acc.concat(stmts)));

// We're sending a complete statement, however it lacks an ending
// newline character. Hence, `incomplete` will be `true`.
interactive.next("echo 'foo'"); // []
console.log(interactive.incomplete); // true

// We finalize the previous statement by sending a newline character
// and receive the parsed `Stmt`. It is also sent to all subscribers.
interactive.next("\n"); // [Stmt {}]
console.log(interactive.incomplete); // false

interactive.next("echo 'ba"); // []
console.log(interactive.incomplete); // true

interactive.next("r'\n"); // [Stmt {}]
console.log(interactive.incomplete); // false

// There's two statements here, one of which (the second) is incomplete.
// Since we don't have a finishing newline character, we don't get
// the parsed statements until the second statement is finalized
// and a newline character is sent.
interactive.next("echo 'baz'; echo 'foob"); // []
console.log(interactive.incomplete); // true

interactive.next("ar'\n"); // [Stmt {}, Stmt {}]
console.log(interactive.incomplete); // false

// In contrast, if we had a separating newline character, we'd receive
// the first completed statement
interactive.next("echo 'baz';\necho 'foo'"); // [Stmt {}]
console.log(interactive.incomplete); // true

interactive.next("\n");
console.log(interactive.incomplete); // false

console.log(acc); // [ Stmt {}, Stmt {}, Stmt {}, Stmt {}, Stmt {}, Stmt {} ]
interactive.end(); // [ Stmt {}, Stmt {}, Stmt {}, Stmt {}, Stmt {}, Stmt {} ]

Traversal

const { Parser, traverse, Printer } = require('./package/pkg');

const parser = new Parser();
const tree = parser.parse("echo 'foo'");

traverse(tree, (node) => {
  if (node.type === 'SglQuoted') {
    node.value = 'bar';
  }
  return true;
});

const printer = new Printer();
console.log(printer.print(tree)); // echo 'bar'

JSON

Serialization

const { Parser } = require('./package/pkg');

const parser = new Parser({ language: 'POSIX' });
const tree = parser.parse('echo "foo bar"');

console.log(JSON.stringify(tree, null, 2));

Deserialization

const { Parser, fromJSON, Printer, File } = require('./package/pkg');

const parser = new Parser({ language: 'POSIX' });
const tree = parser.parse('echo "foo bar"');

const printer = new Printer();

// We can parse a stringified JSON and reconstruct it
// regardless of its root type via fromJSON().
// `a` will be equal to `tree`.
const a = fromJSON(JSON.parse(JSON.stringify(tree)));
console.log(printer.print(a)); // echo "foo bar"

// We can also just convert instances to and from plain objects
const b = fromJSON(tree.toJSON());
console.log(printer.print(b)); // echo "foo bar"

// If we knew the type of the root node, we could use the constructor
// fromJSON() method. As the tree root is an instance of `File`, we'd do:
const c = File.fromJSON(tree.toJSON());
console.log(printer.print(c)); // echo "foo bar"

Build

$ git clone https://github.com/rafamel/shst.git
$ cd shst
$ npm install
$ npx kpo @package build

You can also build the docs via npm run @root -- docs.

Pending

  • Tests.
  • exec() should return an object of actions to perform.

Credits

This library provides a JS native api and TypeScript typings for the originally Go module sh/syntax, by @mvdan