### HTML tags in templates
The plain JavaScript in the file is absolutely normal and is expected to use
-CommonJS conventions, for instance require(...) is supported and so forth.
+CommonJS conventions, for instance, `require(...)` is supported and so forth.
-The embedded HTML uses a syntax similar to JavaScript's function syntax,
+The embedded HTML uses a syntax similar to JavaScript function definitions,
`function(arg1, arg2, ...) { ... }`
except that instead of the word "function" any valid HTML (or other) tag name
is allowed, and instead of just argument names, HTML attributes of the form
`attrname`
or
`attrname=value`
-are supported. In the `attr=value` syntax, the `value` is any valid JavaScript
+are supported. In the `attrname=value` syntax, `value` is any valid JavaScript
expression. No commas are required between attributes, so the parser has to
automatically detect where one expression ends and another begins (similarly to
how automatic semicolon insertion works in regular JavaScript).
<html lang="en"><body>Hello, world</body></html>
```
+If the text is multi-line, backquoted (template) strings are be used, to allow
+embedded newlines. When the entire text is held in a variable, e.g. `myvar`, a
+template string such as `\`${myvar}\`` should be used, to convert it into a
+standalone statement consisting of only a string contant, and hence into HTML.
+In the future, we may extend the parser to do this automatically for statements
+that consist of only a variable name, which would have no effect in JavaScript.
+
+Note that ordinary single- or double-quoted string constants should be used in
+preference to template strings where possible, since the constant strings will
+be HTML-escaped at template compile time rather than at template run-time.
+
Note that in ordinary HTML, certain tags are more sensitive to whitespace than
others, according to complicated rules about inline block elements and so on.
This is never an issue with JavaScript templates, we can use as much indenting
and other whitespace as we want, and only quoted whitespace will be output.
-### Shorthand for classes and IDs
+### Special tags in templates
+
+The HTML `script` tag is treated specially, because it contains JavaScript,
+which is understood directly by the template parser. The JavaScript inside the
+script tag is normalized (comments stripped, unnecessary braces and parentheses
+removed, semicolons added and so on), and then converted to a string constant,
+which will be copied out to the HTML file whenever the template is executed.
+
+Code within `script` tags is output without any indentation, which reduces the
+wasted space in the HTML file quite considerably. In the future, we can add
+automatic minification and/or other transformations, such as ES5 transpilation.
+As well as browser-oriented transformations, other potential transformations
+such as TypeScript could be applied to all code rather than just `script` tags.
+
+The HTML `style` tag is currently not handled specially, thus the content will
+most likely be given as a backquoted (template) string, to support embedded
+newlines. To defeat the HTML escaping, it can be wrapped in `_out(...)`, which
+will be explained further below. In the future, we will switch to a dedicated
+CSS parser, or potentially the `less` or `sass` compiler, for `style` tags.
+
+### HTML class and id shorthand
The tag name can be followed by `#name` for as shorthand for the HTML attribute
`id="name"`
### Parser limitations
-Certain tag or attribute names present difficulty since they contain "-" signs
+Certain tag or attribute names present difficulty since they contain `-` signs
or other characters invalid in JavaScript identifiers, or they may be reserved
-words in JavaScript. The parser copes with this quite well (many syntax errors
+words in JavaScript. The parser copes with this quite well (most syntax errors
can already be re-parsed as tag or attribute names), but in difficult cases it
could be necessary to quote the tag and/or attribute names. For example,
`div.class-1.class-2 {}`
than normal in parsing regular JavaScript code, for instance commas are not
required between function arguments, because HTML templates are basically
parsed as function calls until we see the opening `{` that identifies it as a
-template. This can also likely be improved in a future `js_template` version.
+template. This can also likely be improved in a future version of the system.
## Expression vs statement templates
It is also possible to use statement-level HTML templates or strings to build
up an output buffer, which can be used for whatever purpose. The output buffer
-must be called _out and it must be a JavaScript array of string fragments. As
+must be called `_out` and must be a JavaScript array of string fragments. As
each output fragment (such as an opening or closing tag or an embedded string)
is generated, it will be sent to the output buffer by an `_out.push(...)` call.
If there is dynamically generated text in the template, then there must be a
function `_html_escape()` provided in the current scope, that escapes the text.
-For example, consider a realistic template which we use on our web server:
+For example, consider a realistic template which we use on a our demo server:
```
html(lang=lang) {
head {
should be parsed and any HTML constructs converted into regular JavaScript; and
(4) The template importing is an asynchronous operation, so you normally use
-`await _require(...)` whereas `require()` is synchronous. Because templates can
-be changed and recompiled automatically while the webserver is serving pages,
+`await _require(...)`, whereas `require()` is synchronous. Because templates
+which have been edited are recompiled automatically by the running webserver,
we don't want to stop the world, especially as templates are slow to compile.
A slight difference between CommonJS exports and JavaScript template exports,
standard HTML tags to create a page. Many templating systems work like this,
so that you can put standard scripts, viewport commands etc, in one place.
-Save this as `page.jst`, as it's the template for all pages in the system:
+Save this as `page.jst`, since it's the template for all pages in the system:
```
return body_text => html {
head {
### Templating with callbacks
Another way, which is more sophisticated, is to use a callback function to
-generate the body. If doing this, we pass the output buffer _out into the
+generate the body. If doing this, we pass the output buffer `_out` into the
callback function, so that the entire HTML of the page can be generated in an
-unbroken stream, without having to concatenate the partial page repeatedly:
+unbroken stream, without having to concatenate the partial page repeatedly.
The page wrapper template `page.jst` which takes a body callback and calls it:
```
}
}
```
+
Then the specific page template `index.html.jst` which provides the callback:
```
let page = await _require('/page.jst')
```
Note that in the above example, the `{ }` around the callback function body is
-essential (unlike in regular JavaScript), to make `p {...}` occur in statement
+essential, unlike in regular JavaScript, so that `p {...}` occurs in statement
rather than expression context. As an expression it would return a string which
would be ignored, since the page template is expecting output to be in `_out`.
}
}
```
-Suppose the above is saved as `/hello/index.html.jst` under your document root,
+Suppose the above is saved as `/hello/index.html.jst` under your document root
`/var/www/html`. You call `js_template(root, dirname, pathname)`, like this:
```
let template_func = js_template(
As well as this, the compiled templates are also cached on disk, which requires
the document root to be writeable. For instance if you compile `index.html.jst`
in a `hello/` subdirectory of the document root, you get a hidden file with the
-same name and a `.js` subscript, here it would be `hello/.index.html.jst.js`.
+same path and a `.js` subscript, here it would be `hello/.index.html.jst.js`.
The main reason for the disk caching is really to fool the node.js `require()`
system into using correct relative paths, if the template uses `require()` as
well as `_require()`. However, it is also handy that the templates only need to
-be re-evaluated and not recompiled when the webserver is stopped and restarted.
+be re-evaluated and not recompiled if the webserver is stopped and restarted.
Before using either a disk-cached or a memory-cached template, the modification
times are checked and the template is recompiled if it is stale. Thus you can
edit your website while it is live, and each page will be recompiled as needed
-each time it is served. Templates deleted on disk are deleted from the cache.
+each time it is served. Templates deleted on disk are also hidden in the cache.
-Note: deletion of stale pages from the memory cache is not implemented yet (it
-will use a long timeout such as one week), so the webserver should be restarted
-occasionally. This will be fixed if it becomes an issue in practice. Also, if
-the document root is not writeable, there is a simple expedient of making sure
-that all the needed `.jst.js` files are up to date, so no write is attempted.
+Note: deletion of rarely or never accessed pages from the memory cache is not
+yet implemented (it will use a long timeout, e.g. one week), so the webserver
+should be restarted occasionally. This will be fixed if it becomes an issue in
+practice. Also, if the document root is not writeable, a simple expedient is to
+make sure that all `.*.jst.js` files are up to date, so no write is attempted.
## Conclusions