Asynchronous, streaming HTML templating for Node.js and the browser. The same
templates that pre-generate your HTML5 and stream the results are then run in
the browser. You can push data through them and they'll update the DOM.
Routing is file system driven. The directory path is the URL path.
Routes are transported to the browser so you can have a single page pushState
application and SEO friendly server-side generated pages on the first visit.
What does all this cost you? Not much. The template and routing engine are tiny
at less than <5k. The same engine that runs in the browser runs in the server
in Node.js and it runs fast. No PhantomJS trickery.
The template engine is both asynchronous and streaming.
And the best part about Stencil; there's not a thing MVC about it!
You don't need to build an object to feed the template engine. If you add a tag
that needs an asynchronous data, the call is made by the template. If you remove
that tag, the call is removed. You don't have to update three artifacts to
remove the call.
Asynchronous calls are transparent. There are no callbacks expressed in the
template themselves. Anyone familiar with PHP ought to be able to author
templates.
Plus, you get tag libraries and layout libraries to further encapsulate
asynchronous calls. You can build a tag library to hand off to a web team
familiar with PHP or JSP. They'll be writing highly-asynchronous Node.js
programs and they won't even know it.
Managing resources is simple and done via Browserify so that of the plumbing of
your Stencil application has a Node.js feel to it. Browserify bundles your
application JavaScript and Stencil templates and sends it in a single request to
overcome latency.
Finally, Stencil is an API first web framework. Templates on pull their data
from your API whether they run on the server or in the browser. You can build
your API right along side your Stencils using Register, a CGI-like API framework
that uses the same file system based routing logic as Stencil.
And We're Done
I'm updating Stencil now. Here's where I'm at...
I build Stencil as a MicroJS template library, but the
main goal was templates that run on both the browser and server. The server part
makes Stencil silly as a MicroJS templating language. The Stencil engine
supports tags and layouts organized into libraries. That organization needs an
organizational convention. The Stencil engine can run on the server, which
implies a default Stencil server. They Stencil engine can run in the browser,
which implies a pushState library to invoke the Stencils.
Thus, I find myself writing a web framework. The core is still a MicroJS
library, but the Stencil template engine is not a simple string templating
engine. No one would ever use it without scaffolding.
Now the base engine is so small because it is built on top of XML. The templates
are XML templates. The based template language is an XML language. Here's
another problem for adoption. People hate XML. No one more than myself.
Thus, the current state of Stencil is that I'm writing a template language
called the Stencil language that compiles into XStencil. The Stencil language
looks like ERB and I hope will feel like some of ye olde Perl templating
languages that I once loved (I yearn for you tragically, Mason.)
The same templates run on the browser and in Node.js, so you can use the same
logic you use to serve a generate page to refresh that page.
The Rest is XStencil
Sorry, folks. The rest is XStencil. I'm going to create Stencil and XStencil
examples of everything and let you toggle between languages. You'll want to use
Stencil, but there are some applications, Facebook I believe, that still use
XHTML, so until I sort out support for Facebook, I'm going to keep all the
XStencil examples I created, and consider how to serve XHMTL.
But, I hate XML more than a pocket full of angry bees, so if I can make XML go
away completely, I shall. (But, it won't. The Stencil engine is tiny because of
XML.)
Stencil on the Server
On the server, we create a template and serialize it to HTML5. You can also
serialize to older HTML flavors for older browsers.
Stencil on the Browser
On the browser, when we generate Stencil XML, we simply import it into the
existing DOM using Document.adoptNode.
The value Directive
The value directive inserts a value from the context into the DOM. The value
attribute takes a select property. The select property is evaluated as a
JavaScript statement. The return value of the statement is converted to string
and added to the DOM.
Here we evaluate the JavaScript expression 1 + 1.
TK: Side by side; like a boss!
<html><body><p><%=1+1%></p></body></html>
This generates a document with the statement calculated.
2
Of course, in practice, you're going to want to insert the values from the
object you've pushed through the stencil.
Greetings , and welcome to the bureaucracy.
This template assumes a person property has been added to the context object.
Greetings Kafka, Franz
and welcome to the bureaucracy.
You can also insert HTML into your document using the value directive by
setting its type attribute to html. Generally, you want to put your HTML in
your stencil, not in your data, but there are some exceptions. You might be
generating HTML from user supplied markdown, or you might be rendering data that
has been edited by a rich-text editor, like in a content management system.
Here's a ridiculous example, we've placed a JavaScript string with HTML in the
select attribute and set the type attribute to html.
Hello, World!' %>
The select attribute value is evaluated as JavaScript, so we need the inner
quotes, because it is a JavaScript string. The string is then parsed as HTML and
the HTML is inserted into the document.
Hello, World!
Of course, you're not going to hard code HTML into a value directive like this
but instead insert HTML from a property in your context object.
The with Directive
The with directive is almost exactly like the each directive, except that it
always operates on an object. When you pass an object or scalar, it will assign
the value to the variable named in the as property.
The each directive also does this, treating an object or scalar as an array of
one item, but with is going to make a lot more sense when you read your
templates.
Note that when you use with to select an Array value, the array is not
iterated. The array is assigned to the variable specified by the as attribute;
not the array values.
The when Directive
The when directive is used to only update a section of a template if the data
is available. When the when directive is encountered during an update, the
select attribute is evaluated, if it evaluates to true, then the contents of
the when directive are re-evaluated. It it evaluates to false, then the
contents are left as they are.
This is useful if you have generated a page and are checking the server for
updates, but only to certain dynamic sections of the page.
If, for example, you have a blog page that has an article and a sidebar, you
might be updating the sidebar with recent comments. You want to push a new
collection of recent comments through your Stencil, but if you don't also
provide the article, the Stencil will erase your article.
Here's how we avoid that.
By on Recent Comments ~ on
The when directive is primarily for updates, since it is assumed that you'll
have all your data on hand when you first generate the page on the server side.
However, if during generation a when directive evaluates to false, it
behaves as an if directive, removing the content. It will then generate the
content on the next update where the when directive evaluates to true.
TODO: Should the when directive support else?
The Context Object
From within your templates you can reference the context object itself either by
referencing this or the special variable dollar sign $ with is an alias for
for this.
You can also use the dollar sign $ alias, if you find that more aesthetically
pleasing. TK: Huh? Am I still doing this?
But, again, why? Only because I'm doing this stupid thing with prototypes that
I'm going to try to document right here...
The stencil Object
The stencil variable contains a special object added to the context by the
Stencil that has properties of the current template. The stencil variable is
constantly reset and overrides and variable assignments by the user.
The stencil Object offers the following properties and methods.
url — The URL of the current Stencil.
absolutize(base, path) — Creates an absolute path using the given
base and sub-path. If the sub-path begins with a /, it is treated as a full
qualified path.
normalize(path) — Normalizes a file path so that
/docs/./articles/../posts/post.html is normalized to
/docs/posts/post.html.
json(url) — Returns a Stencil callback that will fetch the given URL
and parse it as JSON.
TK: Find a place to write more about json(url) and send the readers of this
section to that place.
Tag Libraries
Using the same language as used in templates, Stencil supports the creation of
tag libraries. With tag libraries you can doe more to hide the complexity of a
template. You can create tags that name collections in application domain.
Passing Parameters to Blocks
When you create tags you're going to want to have them add values to the
caller's template context, TK what? blech! ... many useful tags are going to
fetch data and then expose it to the caller. This is done with a params
attribute on the block directive. Use the params directive to define a
single JavaScript object that will be visible to the caller.
You can all this tag from your template and it will create a property in the
template context named $numbers, that is, the tag name with dollar sign $ in
front of it.
Asynchronous, streaming HTML templating for Node.js and the browser. The same templates that pre-generate your HTML5 and stream the results are then run in the browser. You can push data through them and they'll update the DOM.
Routing is file system driven. The directory path is the URL path.
Routes are transported to the browser so you can have a single page
pushState
application and SEO friendly server-side generated pages on the first visit.What does all this cost you? Not much. The template and routing engine are tiny at less than <5k. The same engine that runs in the browser runs in the server in Node.js and it runs fast. No PhantomJS trickery.
The template engine is both asynchronous and streaming.
And the best part about Stencil; there's not a thing MVC about it!
You don't need to build an object to feed the template engine. If you add a tag that needs an asynchronous data, the call is made by the template. If you remove that tag, the call is removed. You don't have to update three artifacts to remove the call.
Asynchronous calls are transparent. There are no callbacks expressed in the template themselves. Anyone familiar with PHP ought to be able to author templates.
Plus, you get tag libraries and layout libraries to further encapsulate asynchronous calls. You can build a tag library to hand off to a web team familiar with PHP or JSP. They'll be writing highly-asynchronous Node.js programs and they won't even know it.
Managing resources is simple and done via Browserify so that of the plumbing of your Stencil application has a Node.js feel to it. Browserify bundles your application JavaScript and Stencil templates and sends it in a single request to overcome latency.
Finally, Stencil is an API first web framework. Templates on pull their data from your API whether they run on the server or in the browser. You can build your API right along side your Stencils using Register, a CGI-like API framework that uses the same file system based routing logic as Stencil.
And We're Done
I'm updating Stencil now. Here's where I'm at...
I build Stencil as a MicroJS template library, but the main goal was templates that run on both the browser and server. The server part makes Stencil silly as a MicroJS templating language. The Stencil engine supports tags and layouts organized into libraries. That organization needs an organizational convention. The Stencil engine can run on the server, which implies a default Stencil server. They Stencil engine can run in the browser, which implies a
pushState
library to invoke the Stencils.Thus, I find myself writing a web framework. The core is still a MicroJS library, but the Stencil template engine is not a simple string templating engine. No one would ever use it without scaffolding.
Now the base engine is so small because it is built on top of XML. The templates are XML templates. The based template language is an XML language. Here's another problem for adoption. People hate XML. No one more than myself.
Thus, the current state of Stencil is that I'm writing a template language called the Stencil language that compiles into XStencil. The Stencil language looks like ERB and I hope will feel like some of ye olde Perl templating languages that I once loved (I yearn for you tragically, Mason.)
Your Input Is Welcome
Thoughts on Stencil? Join the Discussion.
Synopsis
Minimal.
Layouts.
Pages laid out.
The same templates run on the browser and in Node.js, so you can use the same logic you use to serve a generate page to refresh that page.
The Rest is XStencil
Sorry, folks. The rest is XStencil. I'm going to create Stencil and XStencil examples of everything and let you toggle between languages. You'll want to use Stencil, but there are some applications, Facebook I believe, that still use XHTML, so until I sort out support for Facebook, I'm going to keep all the XStencil examples I created, and consider how to serve XHMTL.
But, I hate XML more than a pocket full of angry bees, so if I can make XML go away completely, I shall. (But, it won't. The Stencil engine is tiny because of XML.)
Stencil on the Server
On the server, we create a template and serialize it to HTML5. You can also serialize to older HTML flavors for older browsers.
Stencil on the Browser
On the browser, when we generate Stencil XML, we simply import it into the existing DOM using
Document.adoptNode
.The
value
DirectiveThe
value
directive inserts a value from the context into the DOM. Thevalue
attribute takes aselect
property. Theselect
property is evaluated as a JavaScript statement. The return value of the statement is converted to string and added to the DOM.Here we evaluate the JavaScript expression
1 + 1
.TK: Side by side; like a boss!
This generates a document with the statement calculated.
Of course, in practice, you're going to want to insert the values from the object you've pushed through the stencil.
This template assumes a
person
property has been added to the context object.You can also insert HTML into your document using the
value
directive by setting itstype
attribute tohtml
. Generally, you want to put your HTML in your stencil, not in your data, but there are some exceptions. You might be generating HTML from user supplied markdown, or you might be rendering data that has been edited by a rich-text editor, like in a content management system.Here's a ridiculous example, we've placed a JavaScript string with HTML in the
select
attribute and set thetype
attribute tohtml
.The
select
attribute value is evaluated as JavaScript, so we need the inner quotes, because it is a JavaScript string. The string is then parsed as HTML and the HTML is inserted into the document.Of course, you're not going to hard code HTML into a
value
directive like this but instead insert HTML from a property in your context object.The
with
DirectiveThe
with
directive is almost exactly like theeach
directive, except that it always operates on an object. When you pass an object or scalar, it will assign the value to the variable named in theas
property.The
each
directive also does this, treating an object or scalar as an array of one item, butwith
is going to make a lot more sense when you read your templates.Note that when you use
with
to select an Array value, the array is not iterated. The array is assigned to the variable specified by theas
attribute; not the array values.The
when
DirectiveThe
when
directive is used to only update a section of a template if the data is available. When thewhen
directive is encountered during an update, theselect
attribute is evaluated, if it evaluates totrue
, then the contents of thewhen
directive are re-evaluated. It it evaluates tofalse
, then the contents are left as they are.This is useful if you have generated a page and are checking the server for updates, but only to certain dynamic sections of the page.
If, for example, you have a blog page that has an article and a sidebar, you might be updating the sidebar with recent comments. You want to push a new collection of recent comments through your Stencil, but if you don't also provide the article, the Stencil will erase your article.
Here's how we avoid that.
The
when
directive is primarily for updates, since it is assumed that you'll have all your data on hand when you first generate the page on the server side. However, if during generation awhen
directive evaluates tofalse
, it behaves as anif
directive, removing the content. It will then generate the content on the next update where thewhen
directive evaluates totrue
.TODO: Should the
when
directive supportelse
?The Context Object
From within your templates you can reference the context object itself either by referencing
this
or the special variable dollar sign$
with is an alias for forthis
.You can also use the dollar sign
$
alias, if you find that more aesthetically pleasing. TK: Huh? Am I still doing this?But, again, why? Only because I'm doing this stupid thing with prototypes that I'm going to try to document right here...
The
stencil
ObjectThe
stencil
variable contains a special object added to the context by the Stencil that has properties of the current template. Thestencil
variable is constantly reset and overrides and variable assignments by the user.The
stencil
Object offers the following properties and methods.url
— The URL of the current Stencil.absolutize(base, path)
— Creates an absolute path using the given base and sub-path. If the sub-path begins with a/
, it is treated as a full qualified path.normalize(path)
— Normalizes a file path so that/docs/./articles/../posts/post.html
is normalized to/docs/posts/post.html
.json(url)
— Returns a Stencil callback that will fetch the given URL and parse it as JSON.TK: Find a place to write more about
json(url)
and send the readers of this section to that place.Tag Libraries
Using the same language as used in templates, Stencil supports the creation of tag libraries. With tag libraries you can doe more to hide the complexity of a template. You can create tags that name collections in application domain.
Passing Parameters to Blocks
When you create tags you're going to want to have them add values to the caller's template context, TK what? blech! ... many useful tags are going to fetch data and then expose it to the caller. This is done with a
params
attribute on theblock
directive. Use theparams
directive to define a single JavaScript object that will be visible to the caller.You can all this tag from your template and it will create a property in the template context named
$numbers
, that is, the tag name with dollar sign$
in front of it.```erb