kirbysayshi/vash

RFC: Changes coming in vash 0.8.x

Closed this issue · 12 comments

I'm currently in the process of rewriting vash to be more extensible and stable, and to fix a few bugs (#26 especially, #27 to an extent) that are currently neigh unfixable or would be huge hacks with the current architecture. While the parser is becoming more complex, the lexer and compiler are becoming much more simple. In addition, vash will gain true HTML awareness, including HTML attributes and proper void tag support.

Syntax-wise, a template that compiled with 0.7 should compile with 0.8. I also plan to keep the 0.7 branch around for awhile until 0.8 stabilizes, assuming I get some feedback. I'll reply back here when the 0.8 branch is created and ready for testing (the ksh/stated branch is close, but you'll have to require('vash/experiments/stated') with a local repo to get things working).

The current work is being done here: https://github.com/kirbysayshi/vash/tree/ksh/stated/experiments/stated. All tests are currently passing (some with a few modifications), and I plan to move these files into their true home (/src or maybe /lib) very soon to being shoring up the build steps final patterns for third party inclusion.

Since this is all brand new, I also want to take some time to revisit a few things:

Build Process / Shipped Builds

Currently quite custom, manually concatenating everything. The 0.8 version will be using CJS, and thus in nodejs the code will just be the files (require('vash') will point at an index.js instead of a build/vash.js), while in browserland I'll ship builds using browserify and its standalone flag. I assume those using AMD will be able to consume vash just fine through the use of a shim or configuration. And if browserify is being used by a consumer they'll just be able to require('vash') or require('vash/runtime') with no configuration required.

Helper System / Runtime

The helper and buffer API will be remaining the same, but I'd like to enable browser builds to just require('vash/runtime') followed by requireing whatever helpers they actually need. This will likely entail adding a registerHelper method to the runtime or something similar. I'm open to ideas! In addition, I'd like to remove the 'masquerading as full vash' the runtime currently does, but not sure if it's possible without large api changes.

AST

I know of no tools that rely on vash 0.7's concept of an AST, but if there are, they will be completely broken in 0.8. I've created an AST format that is much closer to the SpiderMonkey AST format, although simplified and augmented to be able to handle HTML. It's simplified in the sense that vash does not need full JS knowledge. Please let me know if this is a concern for any tools I do not know about.

Streaming file support

This is not implemented yet, but I planned on vash being able to stream a file while parsing / lexing, instead of it's current "buffer the whole file" before parsing. The ground work is there, it's more a matter of hooking it up to some actual streaming files and smoothing out the rough edges. Except in cases of very large templates, this is more of a neat feature than a must-have.

I currently use vash templates to generate static content. It doesn't seem that this is currently a supported scenario, but I was able to make it work by using some undocumented functions.

For example, currently I do this at the global level:

vash.config.settings = vash.config.settings || {};
vash.config.settings.views = path.resolve('./views');

And then when I render the template I do it like this:

var rendered = tpl(model, function sealLayout(err, ctx) {
    ctx.finishLayout(); // TODO: undocumented
});

Me too! I'm also planning on using it inside a product (Windows desktop application) to generate static HTML files to be later uploaded to a webserver (through a completely other component).

Altough I'm just starting to evaluate your engine, I would love to have everthing that makes generating static files for local disk storing easier 👍

@pstephens, that's awesome, and although undocumented, is pretty much how it's supposed to work. One thing I might change in 0.8 is that the layout helpers may become a separate package, but should keep the api the same.

@UweKeim if you do find things working or not, please let me know so I can try to make it easier. Right now static files are pretty easy, although when you start using the layout helpers they get a bit more complex.

You should be able to do something like:

$ vash --json '{ "settings": { "views": "path/to/views/root/" } }' --file main-static-entry.vash --render --out index.html

Thanks, @kirbysayshi! Being new to GitHub, I've put a feature request here since I did not find a "real" discussion forum. Is this the correct way of doing it?

Yes, vash's GH issues area also functions as a discussion forum. I thought it best to keep things consolidated.

Hey @kirbysayshi, any ETA on when 0.8 might be rolling out? I'm starting a new project soon'ish and would love to have 0.8 included from the start. I'm mostly interested in the streaming support because I'd like to build my response pipeline around streams.

@dizlexik Contrary to what I said above, the next version will likely not have streaming support, as it's already taken too long to get out and I haven't actually implemented it yet. But in the meantime, you can probably do something like this to "fake" stream, depending on what you actually want streaming to mean:

(warning, untested code)

var vash = require('vash');
var through = require('through2');

somestream.pipe(vashstream()).on('data', function(tpl) {
  tpl({ blah: 'ok?' });
});

function vashstream() {
  var buf = '';
  return through(function(chunk, enc, cb) {
    buf += chunk;
    cb();
  }, function(cb) {
    this.push(vash.compile(buf));
    cb();
  });
}

btw @dizlexik, what does streaming support mean to you? The parsing/compilation of the template, or the rendering? I'm guessing only the parsing/compilation is doable as a stream.

Thanks for the stopgap code. I'll be using Koa and was hoping for something to the effect of this.body = tpl.stream(mymodel). My goal is to start sending my response immediately and not have to buffer the entire thing into memory first. Although the more I think about it the more I'm not even sure that makes sense with vash since there's really no concept of async operations within a template as far as I'm aware.

That's where my thinking has been leading as well: I'm not sure streaming has a significant value for a template that is essentially synchronous.

One thing I haven't experimented with at all is generators within a template, which would then blow the entire synchronous model apart. Perhaps a future version of vash...

~ Drew

On Mar 2, 2015, at 2:21 PM, Joe Lutz notifications@github.com wrote:

Thanks for the stopgap code. I'll be using Koa and was hoping for something to the effect of this.body = tpl.stream(mymodel). My goal is to start sending my response immediately and not have to buffer the entire thing into memory first. Although the more I think about it the more I'm not even sure that makes sense with vash since there's really no concept of async operations within a template as far as I'm aware.


Reply to this email directly or view it on GitHub.

Going to close, as 0.8.0 has been released with a mostly unchanged api.

I've been looking into Koa recently, and came across the Marko template engine that does streamed template rendering and supports async rendering of HTML fragments as it leverages progressive rendering in the browser. Check out this short screencast about it.

This looks interesting and maybe can provide some inspiration for Vash to support similar features :)