qmjs

0.0.4 • Public • Published

qmjs (?js)

Run JavaScript inline with text, allowing code sections to echo text into the document. Think <?php ?>.

qmjs parses text files for <?js ... ?> blocks, and executes their contents as JavaScript. The strings echoed by the script in each code section are inserted in-place in the output document.

Examples

Echoing

Running a text file containing:

<?js
  echo("qmjs: ");
  echoln("because many services don't like names starting with '?'."); // appends a newline character
  echo("Unfortunately");
?>
I would have preferred '?js'.

through qmjs results in an output file containing:

qmjs: because many services don't like names starting with '?'.
Unfortunately.
I would have preferred '?js'.

Variable scope

You can fill the <?js ?> sections with JavaScript, and have as many sections as you need. Global variables persist between code sections, but local variables (declared with var) do not. Use the this object to persist over code sections in the current input being processed to avoid polluting the global scope (which is shared with include()'ed files).

<?js
  globalVariable = "this IS usable in subsequent code sections";
  this.variable = "this IS ALSO usable in subsequent code sections";
  var localVariable = "this IS NOT usable in subsequent code sections";      
?>
<?js
  echoln(globalVariable);   // OK
  echoln(this.variable);    // OK
  echoln(localVariable);    // Error!
?>

Using built-in formatting helpers

The same qmjs object that you can require() in your own module is available inside code sections. There are Markdown formatting helpers you can use like:

<?js
  md = qmjs.markdown;
  echo(md.h1("Heading 1"));
  echo(md.orderedList([ "a", "b", [ "b0", "b1" ], "c" ]);
?>

That result in output like:

# Heading 1

* a
* b
    * b0
    * b1
* c

See Formatting helpers for lists of what is available.

Including other files verbatim

Using includeText() like:

<?js
  includeText('./README.md');
?>

Echoes the contents of files into the output text.

Asynchronous flow

echo() can be used in asynchronous callbacks by first calling the wait() function, and then signalling that the code section is complete by calling the returned function. For example:

<?js
  var done = wait();
  
  echoln("This is the start of a code section.");
  
  setTimeout(function() {
    echoln("This is echoed 1 second later.");
    done(); // Don't forget this
  }, 1000);
  
  echoln("This is the end of the code section.");
?>
<?js
  echoln("This is the next code section.");
?>

Outputs:

This is the start of a code section.  
This is the end of the code section.
This is echoed 1 second later.   
This is the next code section.

You may use as many wait()/done() pairs as required in each code section.

Note: If using qmjs programatically, you must use the async versions of qmjs() and qmjs.file() (see Usage API) otherwise errors will be reported.

Including other qmjs files

Given a file e.g. LoremIpsum.qmmd:

# This heading is included from LoremIpsum.qmmd

<?js
  echo("Echoed by JS in LoremIpsum.qmmd: ");
  echoln("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");

  exports.secondSentence = "Donec a diam lectus.";
?>

Synchronously

The return value of the includeSync() method is the exports object from LoremIpsum.qmmd:

<?js    
  var lorem = includeSync("./LoremIpsum.qmmd");
  echoln();
  echo("Echoed by JS in main file: ");
  echoln(lorem.secondSentence);
?>

Note: If the file being included uses wait(), you must use the asynchronous include().

Asynchronously

The asynchronous include() optionally takes a callback function that receives an error argument and the exports object. The async wait() and completion signalling is handled within include().

<?js       
  include("./LoremIpsum.qmmd", function(error, lorem) {        
    echoln();
    echo("Echoed by JS in main file: ");
    echoln(lorem.secondSentence);
  });            
?>

Output

# This heading is included from LoremIpsum.qmmd

Echoed by JS in LoremIpsum.qmmd: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 
Echoed by JS in main file: Donec a diam lectus.

Node's require() and fs

The current working directory is temporarily changed to that of the text file being processed, so any relative file paths used for reading/writing are relative to that.

The require() function is available, but is modified to first search relative to the text file currently being processed (for relative paths), and in the node_modules folder in the first parent directory of the file being processed that contains node_modules.

Usage

To install:

npm install qmjs

CLI

To run the command line executable:

qmjs [INPUT_FILENAME] [options]

If INPUT_FILENAME is not specified, qmjs will read from stdin until it is closed.

Options:

  • -o OUTPUT_FILENAME -- Path to the desired output file. If not specified, output is sent to stdout.
  • --force -- Set this flag to force continue processing when exceptions are thrown (error messages are instead inserted into the document). Otherwise will halt on exceptions.
  • -token.start START -- Token as string, or multiple listed in an array literal, that signal the start of a code section. Defaults to <?js.
  • -token.end END -- Token as string, or multiple listed in an array literal, that signal the end of a code section. Defaults to ?>.

These command line options, and any other user specified ones, are accessible in code sections in the opts object. For example, the opts object can be echoed into the document with:

<?js
  echoln(JSON.stringify(opts, null, 2));
?>

Periods in option keys resolve to object paths, and values can be JSON that contains no spaces. See dot-argv for details.

API

In addition to the command-line program, you can also use qmjs as a module:

var qmjs = require("qmjs");

Synchronous

The string input version:

var outputText = qmjs(inputFileText, opts); // -> string

The version that reads from a file:

var outputText = qmjs.file(inputFileName, opts) // -> string

These synchronous functions can throw errors.

Asynchronous

The string input version:

qmjs(inputFileText, opts, function(error, output) {
  // output is string
});

The version that reads from a file:

qmjs.file(inputFileName, opts, function(error, output) {
  // output is string
});

With the opts argument as an object containing the command-line options, that defaults to:

{
  "force": false,
  "tokens": {
    "start": "<js",
    "end": "?>"
  }      
}

Optionally, you can provide:

  • opts.module that will be used as the executed code's module object, which expects an exports property.
  • opts.inputFilename that will be used for error reporting, and all file read/write will be relative to that path.

And any other variables you need to provide to the code sections through the opts object.

Formatting helpers

Markdown

qmjs.markdown provides these methods and variables to aid creating Markdown:

  • h1(str, padNewlines=true)
  • h2(str, padNewlines=true)
  • h3(str, padNewlines=true)
  • italics(str)
  • bold(str)
  • boldItalics(str)
  • code(str) - inline code
  • codeBlock(str)
  • json(obj)
  • img(path, [altText, title])
  • link(url, [text])
  • orderedList(array, padNewlines=true)
  • unorderedList(array, padNewlines=true)
  • encode(str) - encode HTML entities
  • decode(str) - decode HTML entities
  • endl - new line character
  • hr - horizontal rule

HTML

Implemented and available in qmjs.html object. Check src/formatting/html.js for details.

TODO:

  • wait() timeout
  • HTML formatter doc

Readme

Keywords

Package Sidebar

Install

npm i qmjs

Weekly Downloads

4

Version

0.0.4

License

none

Last publish

Collaborators

  • samuelbr