Skip to content

bryanwayb/js-html

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

52 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build Status Release Version Code Coverage Codacy Grade License

JavaScript template engine for mixing actual JavaScript and text/markup.

<html>
	<body>
		<?js process.stdout.write('Hello, World from JsHtml!'); ?>
	</body>
</html>

Usage

JsHtml script syntax is fairly straightforward.

<?js opens a code block, ?> closes it. A whitespace character must follow the opening tag, but is not required before the closing tag. (e.g. <?jsvar test=0;?> is invalid, but <?js var test=0;?> is okay).

When a code block is only going to be used to output a variable you can use <?js: ?> syntax. Everything contained inside one of these blocks is directly passed to the output function. Spaces are not required for this open tag since : is used to separate the <?jstag and the code block.

There are a few different methods provided of interfacing with JsHtml scripts. Here's one way showing a quick example of the engine in use:

Template

<html>
	<head>
		<title><?js:title?></title>
	<head>
	<body>
		<ul><?js
				for(var i = 0; i < 10; i++) {
					?>
						<li>#<?js:i?></li><?js
				}
			?>
		</ul>
	</body>
</html>

Script

var jshtml = require('js-html');

var script = jshtml.script({
	context: {
		title: 'Example'
	}
});
script.setScriptFile('./example.html');
console.log(script.render());

Render Output

<html>
	<head>
		<title>Example</title>
	<head>
	<body>
		<ul>
						<li>#0</li>
						<li>#1</li>
						<li>#2</li>
						<li>#3</li>
						<li>#4</li>
						<li>#5</li>
						<li>#6</li>
						<li>#7</li>
						<li>#8</li>
						<li>#9</li>
		</ul>
	</body>
</html>

API

jshtml.compile(script) - Takes input JsHtml script string and returns JavaScript string.

jshtml.render(script, [options], [callback]) - Quick method for rendering JsHtml script. This should only be used for one time uses. If a script will be called multiple times use the jshtml.script object instead (see below).

jshtml.cache([cache], script, [options]) - This is the same as jshtml.script() below, except when you'd rather not manage the script objects yourself. Returns a jshtml.script object.

The cache parameter is an identifier used to store the script object. When not provided instead used the script string (so be careful when using this).

jshtml.script([script], [options]) - Create a jshtml.script object. This will cache compiled scripts, contexts, and vm functions and it's use it highly recommended when performance over multiple renders is required. Options and their defaults:

{
	syntaxCheck: true,
	format: false,
	mangle: false,
	optimize: false,
	minify: false,
	context: undefined,
	filename: undefined,
	isolate: true
}

Probably the only options that need explanations are context, filename, and isolate.

context expects an object and allows for passing of additional global variables that JsHtml scripts can use. This also allows for some scripts to share context objects, depending on what variables to make available to them.

filename sets the name of the file to be used when running the JsHtml script. This should be set to an absolute path and is useful for scripts that require __dirname or __filename to be set.

isolate is a bool that, when true, will execute the JsHtml script in a separate context from the calling context, at the cost of a slight amount of execution overhead by the JavaScript engine. If speed is important and context separation isn't a worry, you can disable this.

jshtml.script().setOptions(options) - Set options (see above) for the jshtml.script object.

jshtml.script().setScript(script) - Sets the script string to use for compilation. This calls jshtml.script().clear() before setting the new script string.

jshtml.script().setScriptFile - Quick method for synchronously loading a file as a script. Automatically sets the filename option.

jshtml.script().clear() - Completely reset the script object. This will clear any compiled/uncompiled script, compiled functions, and context. It will only keep the options set either at creation or with jshtml.script().setOptions().

jshtml.script().compile() - Similar to jshtml.compile(), except this will be cached and adds a few more options to be used along with it (such as reformatting script output, minification, etc...).

jshtml.script().makeFunction() - This will compile the currently set script (if it hasn't been already), create a context (again, if it hasn't), and will return function([callback]) that can be called to render the compiled script.

If a callback parameter isn't supplied, the script will be ran synchronously and the return value will be the rendered script. Conversely, passing a callback with an argument for the rendered string will set the script to be ran asynchronously. JsHtml scripts that run asynchronously will need to call this.complete(); when they are finished rendering. Otherwise the callbacks will never be returned.

Here's a simple example with an async JsHtml script:

Hello<?js process.nextTick(function() {
    this.complete();
}); ?>, World

jshtml.script().render([callback]) - A shortcut for calling function([callback]) returned by jshtml.script().makeFunction().


CLI Usage

Command line use has also been provided. Personally I find it useful for testing a script before I incorporate it into a project.

$ jshtml --help
Usage: js-html [files] [options] .. or
       js-html [options] -- [files]

Options

    -r, --render   Render the compiled output instead of stopping after
                   compilation.
    -o, --out      Write output to file instead of stdout.
    --syntax       Enables syntax checking.
    --format       By default compiled code has no formatting applied. This
                   switch enables formatting. Implies --syntax.
    --mangle       Turns on obfuscation for compiled code. Implies --format.
    --optimize     Enables compiled script optimization. Implies --format.
    --minify       Minify compiled output. Implies --mangle.
    -v, --version  Prints the version/author info in the output header
    -h, --help     Prints this help information.

Notice that I used jshtml, but the help says js-html. You can use both as they are both registered to the same command. This was done since the project name is registered as js-html, but referred to as jshtml.


How It Works

The engine itself is pretty simple, practically to the point where it's almost unfair to call it an engine at all and more of an extension library. There are 4 stages to rendering a script: compilation, context initialization, runtime compilation, and lastly execution/rendering.

Input scripts are compiled directly to JavaScript code, with the text sections being passed as strings to the process.stdout.write function. Take the following input:

This <?js:'is'?> a "simple" <?js process.stdout.write('example');

Without any post-compilation formatting, this get's compiled into:

process.stdout.write(('This ')+('is')+(' a \"simple\" ')); process.stdout.write('example');

The JavaScript sections go completely untouched by the compiler. After the compilation, syntax errors are checked by default (although this can be disabled by setting the jshtml.script() object options).

The next step is to create the execution context. Similar to how require() works, the JsHtml script object calls its makeFunction() method which will initialize the context for the script to execute in.

If the isolate option is set to true, the created context is the only scope the script is allowed to access. This makes it so the JsHtml script being executed will not have access to anything from the calling scope unless it is passed specifically. This also means that separate JsHtml scripts do not share a scope with each other unless they are given a shared context object.

After the context creation comes VM compilation. This is where the compiled script is turned from a script string into an executable JavaScript function. This is done using the vm functions provided by NodeJS.

Finally, if both context generation and VM compilation are successful, the script can be executed and the rendered results returned.


Installing

npm

npm install js-html

git

git clone https://github.com/bryanwayb/js-html.git && cd js-html && npm install

Running Tests And Benchmark

JsHtml has been configured for numerous tests to test compatibility with a specific NodeJS version. These test not only ensure basic functionality will be available, but also security in separation of contexts.

Note: NodeJS version 0.11.6 and lower fail tests because contexts are not properly separated in those versions. Passing the { isolate: true } will not yield the expected results. This problem was fixed in 0.11.7 and later.

If you would like to run these tests run the below command while in the modules root directory:

npm test

Benchmarking can also be performed to view how fast the test scripts will execute under different hardware and operating systems (as well as between versions).

npm run bench