qmjs v0.0.4
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
* cSee 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 qmjsCLI
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); // -> stringThe version that reads from a file:
var outputText = qmjs.file(inputFileName, opts) // -> stringThese 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.modulethat will be used as the executed code'smoduleobject, which expects anexportsproperty.opts.inputFilenamethat 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 codecodeBlock(str)json(obj)img(path, [altText, title])link(url, [text])orderedList(array, padNewlines=true)unorderedList(array, padNewlines=true)encode(str)- encode HTML entitiesdecode(str)- decode HTML entitiesendl- new line characterhr- horizontal rule
HTML
Implemented and available in qmjs.html object. Check src/formatting/html.js for details.
TODO:
wait()timeout- HTML formatter doc