batiste / pug-vdom

PUG template to HyperScript Virtual DOM
MIT License
18 stars 6 forks source link

Support Hyperapp textNode handling #51

Open johnkazer opened 3 years ago

johnkazer commented 3 years ago

I use pug-vdom to enable Pug templates to be used with the Hyperapp framework. This worked great, until the framework added a new function text() in parallel with h() to separately process virtual text nodes. As per this snippet h("h1", {}, text("To do list")). I generated a work-around in this repo with a local copy of pug-vdom.

So now the following simple template fails

div
    // receives the <greet> variable from the supplied state object
    - var greeting = "Hello " + greet
    p(style={color: "red"}) #{greeting}
    input(size="60" placeholder=placeholder onchange=handler.updateMe)
    button(id='clickMe' onclick=handler.clickMe) Click Me
    p #{userText}

Because the greeting in n2Child is represented by a string in the following rather than what's returned from h().

  var n0Child = []
  var n1Child = []
  var greeting = "Hello " + greet
  var n2Child = []
  n2Child = n2Child.concat(greeting)
  var props = {attributes: runtime.compileAttrs([{name:'style', val: {color: "red"}}], [])};
  if (props.attributes.id) props.key = props.attributes.id;
  var n2 = h('p', props, n2Child)

For Hyperapp, I need to call text(greeting), making it a virtual textNode as follows:

  var n0Child = []
  var n1Child = []
  var greeting = "Hello " + greet
  var n2Child = []
  n2Child = n2Child.concat(text(greeting))
  var props = {attributes: runtime.compileAttrs([{name:'style', val: {color: "red"}}], [])};
  if (props.attributes.id) props.key = props.attributes.id;
  var n2 = h('p', props, n2Child)

So I would like to change pug-vdom.js to optionally support a text() function as follows, where the generated render(context, h) has a new optional function arg that is used by visitText and visitCode:

Compiler.prototype.bootstrap = function () {
  this.addI(`function render(context, h, text = (string) => string) {\r\n`)
  ...

Compiler.prototype.visitText = function (node, parent) {
  var val = node.val;
  var s = JSON.stringify(val)
  if (val[0] === '<') {
    this.addI(`n${this.parentTagId}Child = n${this.parentTagId}Child.concat(runtime.makeHtmlNode(${s}))\r\n`)
  } else {
    this.addI(`n${this.parentTagId}Child.push(text(${s}))\r\n`)
  }  
}

Compiler.prototype.visitCode = function (node, parent) {
  if (node.buffer) {
    this.addI(`n${this.parentTagId}Child = n${this.parentTagId}Child.concat(${node.mustEscape ? `text(${node.val})` : `runtime.makeHtmlNode(${node.val})`})\r\n`)
  } else {
    this.addI(node.val + '\r\n')
  }

  if(node.block){
    this.addI('{\r\n')
    this.indent++
    this.visitBlock(node.block, node)
    this.indent--
    this.addI('}\r\n')
  }
}
gryphonmyers commented 3 years ago

Sounds reasonable to me. If you make a PR I'll review