baryshev / ect

Fastest JavaScript template engine with embedded CoffeeScript syntax
http://ectjs.com
MIT License
627 stars 70 forks source link

How do I compile to a javascript function that takes a plain javascript object as context and renders it? #55

Open lerouxb opened 10 years ago

lerouxb commented 10 years ago

I'm trying to write a brunch plugin (see http://brunch.io/) to replace my usage of the eco brunch plugin. See here for the implementation of eco-bruch: https://github.com/brunch/eco-brunch/blob/master/lib/index.js

(Mainly so that I can use <% include 'snippet.eco' %> client-side, but I suppose that would require further work.)

Basically I want to mimic this by compiling to a function render(obj) {return 'some text';} style function that can just be converted to code with .toString() and then that gets bundled up with a bunch of other things and sent through to the browser.

I can't find an easy way to achieve that. Am I missing something?

ghost commented 9 years ago

:+1: I was also puzzled that there seems to be no "compile to JS" functionality. I can't replace eco until this feature is in place. Loading a coffeescript template and interpreting live doesn't seem to be a very efficient way to use in-browser.

kuba-kubula commented 9 years ago

@lerouxb @navitmccaffery (maybe @baryshev would implement something similar for ECT as internal part?)

I've got a few pieces of code for you:


ectCompiler = ectRenderer.compiler gzip: false

# simulation of request for ECTjs
class FakeRequestResponse
  constructor: (@url, @cb) ->
    @method = 'GET'
  setHeader: ->
  end: (templateText) ->
    @cb? templateText

# module main Class
class TemplateBundler
  constructor: (options = {}) ->
    {fresh, templates, callback} = options

    fresh              ?= true
    callback           ?= '_callback'

    @alwaysFresh       = !!fresh
    @callbackName      = callback

    @compiledTemplates = {}
    @templatesList     = []
    @compiledAllViews  = false
    if templates?.length > 0
      @addTemplate template for template in templates

  addTemplate: (filePath) ->
    @templatesList.push filePath

  compileOneTemplate: (templateName, done) ->
    # prepare request & response with a method for saving "res.end" call
    req = new FakeRequestResponse '/' + templateName
    res = new FakeRequestResponse '/' + templateName, (compiledText) =>
      # attach template to the object
      @compiledTemplates[templateName] = compiledText
      console.info '⌊ Compiled view `' + templateName + '`, size: ' + compiledText.length
      done null
    # and run these things together
    ectCompiler req, res, (err) ->
      # this callback is run only when a problem occurs. Otherwise res.end callback is used
      if err
        console.error "Failed ECTjs compilation for template `#{templateName}`.", err
      else
        err = new Error("View `#{templateName}` not found when compiling template bundle.")
        log.error "Template `#{templateName}` not found."
      done err

  bundle: (cb) ->
    if @alwaysFresh
      @compiledTemplates = {}
      @compiledAllViews = false
    if @compiledAllViews
      return cb null, @compiledTemplates
    else
      async.each @templatesList, @compileOneTemplate.bind(@), (err) =>
        if err
          @compiledAllViews = false
          log.error 'Bundling all templates failed:', err
        else
          @compiledAllViews = true
        cb err, @compiledTemplates

  # ready for res.send OR saving onto CDN
  bundleJsonP: (cb) ->
    @bundle (err, rootObject) =>
      if err then return cb err
      # replace(*,*) IS copy-pasta from Express > response.js > jsonp
      cb null, "#{@callbackName} && #{@callbackName}(#{JSON.stringify(root: rootObject).replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029')});"

# USAGE
template = new templates.TemplateBundler
  templates: ['docs/views/index', 'docs/views/module/comments', 'docs/views/module/article']
  callback: '_bundleWrapFunction'
  fresh: templates.TEMPLATES_FRESH

template.bundleJsonP (error, fileContent) ->
  if error
    return callback error
  else
    console.log fileContent # or whatever callback of this...