Coder Social home page Coder Social logo

viperhtml's Introduction

viperHTML

viperHTML logo

donate License: ISC Build Status Coverage Status Blazing Fast Greenkeeper badge

Warning

Due low popularity of this project after all these years, and because I have shifted focus to better alternatives, this project is currently in maintenance mode, meaning that 100% feature parity with hyperHTML is not anymore an achievement, and only the simple/obvious will be fixed.

Among various changes happened to hyperHTML, the sparse attributes will likely not be supported, as the refactoring needed here would easily outperform the benefits.

As sparse attributes are never really been a must have, you can simply use attr=${...} and concatenate in there anything you want.

This is also true for most modern alternatives of mine, such as ucontent, but if that's the only deal breaker, have a look at heresy-ssr, which is 1:1 based on lighterhtml, hence likely always 100% in features parity with it, included sparse attributes.


hyperHTML lightness, ease, and performance, for the server.


Don't miss the viperHTML version of Hacker News

Live: https://viperhtml-164315.appspot.com/

Repo: https://github.com/WebReflection/viper-news


Similar API without DOM constrains

Similar to its browser side counterpart, viperHTML parses the template string once, decides what is an attribute, what is a callback, what is text and what is HTML, and any future call to the same render will only update parts of that string.

The result is a blazing fast template engine that makes templates and renders shareable between the client and the server.

Seamlessly Isomorphic

No matter if you use ESM or CommonJS, you can use hypermorphic to load same features on both client and server.

// ESM example (assuming bundlers/ESM loaders in place)
import {bind, wire} from 'hypermorphic';

// CommonJS example
const {bind, wire} = require('hypermorphic');

Automatically Sanitized HTML

Both attributes and text nodes are safely escaped on each call.

const viperHTML = require('viperhtml');

var output = render => render`
  <!-- attributes and callbacks are safe -->
  <a
    href="${a.href}"
    onclick="${a.onclick}"
  >
    <!-- also text is always safe -->
    ${a.text}
    <!-- use Arrays to opt-in HTML -->
    <span>
      ${[a.html]}
    </span>
  </a>
`;

var a = {
  text: 'Click "Me"',
  html: '<strong>"HTML" Me</strong>',
  href: 'https://github.com/WebReflection/viperHTML',
  onclick: (e) => e.preventDefault()
};

// associate the link to an object of info
// or simply use viperHTML.wire();
var link = viperHTML.bind(a);

console.log(output(link).toString());

The resulting output will be the following one:

  <!-- attributes and callbacks are safe -->
  <a
    href="https://github.com/WebReflection/viperHTML"
    onclick="return ((e) =&gt; e.preventDefault()).call(this, event)"
  >
    <!-- also text is always safe -->
    Click &quot;Me&quot;
    <!-- HTML goes in as it is -->
    <span><strong>"HTML" Me</strong></span>
  </a>

Usage Example

const viperHTML = require('viperhtml');

function tick(render) {
  return render`
    <div>
      <h1>Hello, world!</h1>
      <h2>It is ${new Date().toLocaleTimeString()}.</h2>
    </div>
  `;
}

// for demo purpose only,
// stop showing tick result after 6 seconds
setTimeout(
  clearInterval,
  6000,
  setInterval(
    render => console.log(tick(render).toString()),
    1000,
    // On a browser, you'll need to bind
    // the content to a generic DOM node.
    // On the server, you can directly use a wire()
    // which will produce an updated result each time
    // it'll be used through a template literal
    viperHTML.wire()
  )
);

The Extra viperHTML Feature: Asynchronous Partial Output

Clients and servers inevitably have different needs, and the ability to serve chunks on demand, instead of a whole page at once, is once of these very important differences that wouldn't make much sense on the client side.

If your page content might arrive on demand and is asynchronous, viperHTML offers an utility to both obtain performance boots, and intercepts all chunks of layout, as soon as this is available.

viperHTML.async()

Similar to a wire, viperHTML.async() returns a callback that must be invoked right before the template string, optionally passing a callback that will be invoked per each available chunk of text, as soon as this is resolved.

// the view
const pageLayout = (render, model) =>
render`<!doctype html>
<html>
  <head>${model.head}</head>
  <body>${model.body}</body>
</html>`;

// the viper async render
const asyncRender = viperHTML.async();

// dummy server for one view only
require('http')
  .createServer((req, res) => {
    res.writeHead( 200, {
      'Content-Type': 'text/html'
    });
    pageLayout(

      // res.write(chunk) while resolved
      asyncRender(chunk => res.write(chunk)),

      // basic model example with async content
      {
        head: Promise.resolve({html: '<title>right away</title>'}),
        body: new Promise(res => setTimeout(
          res, 1000,
          // either:
          //  {html: '<div>later on</div>'}
          //  ['<div>later on</div>']
          //  wire()'<div>later on</div>'
          viperHTML.wire()`<div>later on</div>`
        ))
      }
    )
    .then(() => res.end())
    .catch(err => { console.error(err); res.end(); });
  })
  .listen(8000);

Handy Patterns

Following a list of handy patterns to solve common issues.

HTML in template literals doesn't get highlighted

True that, but if you follow a simple (render, model) convetion, you can just have templates as html files.

<!-- template/tick.html -->
<div>
  <h1>Hello, ${model.name}!</h1>
  <h2>It is ${new Date().toLocaleTimeString()}.</h2>
</div>

At this point, you can generate as many views as you want through the following step

#!/usr/bin/env bash

mkdir -p view

for f in $(ls template); do
  echo 'module.exports = (render, model) => render`' > "view/${f:0:-4}js"
  cat template/$f >> "view/${f:0:-4}js"
  echo '`;' >> "view/${f:0:-4}js"
done

As result, the folder view will now contain a tick.js file as such:

module.exports = (render, model) => render`
<!-- template/tick.html -->
<div>
  <h1>Hello, ${model.name}!</h1>
  <h2>It is ${new Date().toLocaleTimeString()}.</h2>
</div>
`;

You can now use each view as modules.

const view = {
  tick: require('./view/tick')
};

// show the result in console
console.log(view.tick(
  viperHTML.wire(),
  {name: 'user'}
));

viperhtml's People

Contributors

greenkeeper[bot] avatar kbariotis avatar mroderick avatar webreflection avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

viperhtml's Issues

An in-range update of csso is breaking the build 🚨

Version 3.5.0 of csso was just published.

Branch Build failing 🚨
Dependency csso
Current Version 3.4.0
Type dependency

This version is covered by your current version range and after updating it in your project the build failed.

csso is a direct dependency of this project, and it is very likely causing it to break. If other packages depend on yours, this update is probably also breaking those in turn.

Status Details
  • continuous-integration/travis-ci/push The Travis CI build failed Details

Commits

The new version differs by 5 commits.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

An in-range update of hyperhtml is breaking the build 🚨

Version 2.0.4 of hyperhtml was just published.

Branch Build failing 🚨
Dependency hyperhtml
Current Version 2.0.3
Type dependency

This version is covered by your current version range and after updating it in your project the build failed.

hyperhtml is a direct dependency of this project, and it is very likely causing it to break. If other packages depend on yours, this update is probably also breaking those in turn.

Status Details
  • coverage/coveralls First build on greenkeeper/hyperhtml-2.0.4 at 100.0% Details
  • continuous-integration/travis-ci/push The Travis CI build could not complete due to an error Details

Commits

The new version differs by 2 commits.

  • 81ef4da 2.0.4
  • bfd78c4 Resetting fastPath when mixed content is passed as value.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Style attributes not rendered when values are set programmatically

I am trying to render a style attribute, but it does not show up.

Code similar to the following works in hyperHTML, but not in viperHTML (using Hypermorphic):

const css = { width: "10%" };
bind`<div style="${css}"></div>`;

Expected:
<div style="width: 10%"></div>

Actual:
<div></div>

Slash gets special meaning outside of interpolation

I didn't expect that characters outside of variable interpolation get a special meaning, however it seems viperhtml is doing something special with slashes (/).

Trimmed example:

const view = model => render`
<a href="/${model.loginOrLogout}">${model.loginOrLogout}</a>
`.toString();

const home = (req, res) => {
  const loginOrLogout = 'login'; // an `if` to decide for "logout" will be put later
  const model = {
    loginOrLogout,
  };
  res.send(view(model));
};

Notice the starting slash in the href, when I have that there, I get the following error:

TypeError: updates[(i - 1)] is not a function

If I took it out, page gets properly rendered.

Render templates which are read as string from file.

I can render my template with viperHTML from an implicit template literal.

  res.type('text/html').send(viperHTML.wire()`
  <!doctype html>
  <html lang="en">

  <head>
      <title>title</title>
      <meta charset="UTF-8" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />

  </head>

  <body>

  <div class="test">test</div>
          
  </body>

  </html>
  `);

How would I go along and turn a string read from the same html template file into a template literal for viperHTML?

  const _tmpl = fs.readFileSync(path.resolve(__dirname, '../public/views/hyper.html'), 'utf8');

  res.type('text/html').send(viperHTML.wire()`${_tmpl}`);

updates[(i - 1)] is not a function

Hi,

I have a myFunctionfunction declared inside my HTML

var a = { text: 'A' };
var render = viperHTML.wire();
var result = render`
<script> function myFunction() { alert('hello'); }</script>
<button onclick="myFunction()" >${a.text}</button>
`;
console.log(result.toString());

Giving me this output:

<script>function myFunction(){alert("hello")}</script>
<button onclick="myFunction()">A</button>

This is fine, but, now I want to send a parameter

var result = render`
<script> function myFunction(text) { alert('hello'); }</script>
<button onclick="myFunction('${a.text}')" >${a.text}</button>
`;

and I get the following error

TypeError: updates[(i - 1)] is not a function

and I can't declare the event as in the basic exemple

var a = { ..., onclick: (e) => e.preventDefault() };

I guess, I'm trying to mix different things here, but is it possible ? Could you point me please to the correct syntax I should use ?

note: I'm using this workaroud now

<script> function myFunction(e) { alert(e.getAttribute('data-x')); }</script>
<button onclick="myFunction(this)" data-x="${a.text}">${a.text}</button>

Thanks

viperHTML doesn't seem to preserve white space

Easy enough to replicate, wondering whether it's considered a bug?

const viperHTML = require('viperhtml')

console.log(viperHTML.wire()`<div> Hello, ${'World'} </div>`) // <div>Hello,World</div>

Non adoptable <textarea> / <style>

These two elements don't work well at all with adoptable layout.

It shouldn't really be a common case, and both works well without content, but adoptable should never put comments inside these elements when there is an interpolation.

Possible fix: when updates are created, somehow mark the one related to the interpolation inside textarea or style and do not put the comment in there

Patterns for Components

I tried to make some simple components with viperhtml where the parent just references children. https://glitch.com/edit/#!/comfortable-nail

My first attempt let the component provide its own wire if one wasn't provided. This worked well and was really convenient for maping but I noticed re-renders got increasingly more expensive since the wire wasn't reused.

So, my second attempt used a WeakMap to hold references to the wires and child models rendered.

In my third attempt, https://glitch.com/edit/#!/hickory-pail, I switched to a partially applied component signature that curried the wire. Then rendering children is an exercise of mapping over the child renderers passing the correct model. This was also a convenient pattern and efficient like the second one.

Additional perf gains could be made by having each component check to see if it needs to re-render. If I did that, I would probably want to reuse model objects as well.

For a more complicated child renderer, it would be more efficient to reuse the child renderer and model. So, went back to a WeakMap approach to make sure I reorder the child renderers, not just the model. This would be equivalent to keyed elements in other frameworks. I also think it's probably more efficient to make the children a lazy getter instead of the "simple" approach of updating the children array every time a change happens. I think for a faster render cycle, this would make a difference and work better with raf.

Any ideas, observations, or improvements?

Missing chunks/closing tags

I am working on my viperHTML webpack plugin and just came across a strange issue regarding to how viperHTML sanitzes/minifies the HTML, I have the following code:

const {wire} = require("viperhtml")

console.log(wire()`<  h1   > ${'Bob'} </h1>`.toString())

// "<  h1   > Bob "

https://runkit.com/embed/v4yzhhcvk7g1

I was expecting to get back the sanitized/minified HTML, e.g.:

// "<h1> Bob </h1>"

This is seems to be some error. What is your input on this?

(Feature parity) Trim whitespaces

require("viperhtml")`\n<input>\n`.toString()

hyperHTML trims those \n but viperHTML does not, which causes a difference in DOM tree when parsing the results.

An in-range update of hyperhtml is breaking the build 🚨

Version 2.4.1 of hyperhtml was just published.

Branch Build failing 🚨
Dependency hyperhtml
Current Version 2.4.0
Type dependency

This version is covered by your current version range and after updating it in your project the build failed.

hyperhtml is a direct dependency of this project, and it is very likely causing it to break. If other packages depend on yours, this update is probably also breaking those in turn.

Status Details
  • continuous-integration/travis-ci/push The Travis CI build could not complete due to an error Details
  • coverage/coveralls First build on greenkeeper/hyperhtml-2.4.1 at 100.0% Details

Commits

The new version differs by 2 commits.

  • 5c306bd 2.4.1
  • 5d67ef8 fix #152 - death scripts after tempalte injection

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Feature parity and partial attributes

Latest domtagger introduced partial attributes which are now available in both hyperHTML and lighterhtml.

Since viperHTML is designed to have feature parity with hyperHTML, find the time to implement this in this server side related project too.

viperHTML framework

Is there any plan to develop viperHtml as a complete framework with routing, state management and model based validation like VueJs.

An in-range update of uglify-js is breaking the build 🚨

Version 3.3.0 of uglify-js was just published.

Branch Build failing 🚨
Dependency uglify-js
Current Version 3.2.2
Type dependency

This version is covered by your current version range and after updating it in your project the build failed.

uglify-js is a direct dependency of this project, and it is very likely causing it to break. If other packages depend on yours, this update is probably also breaking those in turn.

Status Details
  • continuous-integration/travis-ci/push The Travis CI build failed Details

Release Notes v3.3.0

 

Commits

The new version differs by 42 commits.

  • f1556cb v3.3.0
  • efffb81 fix comments output & improve /*@__PURE__*/
  • 202f90e fix corner cases with collapse_vars, inline & reduce_vars (#2637)
  • c07ea17 fix escape analysis on AST_PropAccess (#2636)
  • edb4e3b make comments output more robust (#2633)
  • 4113609 extend test/ufuzz.js to inline & reduce_funcs (#2620)
  • 7ac7b08 remove AST hack from inline (#2627)
  • 86ae588 disable hoist_funs by default (#2626)
  • fac003c avoid inline of function with special argument names (#2625)
  • 2273655 fix inline after single-use reduce_vars (#2623)
  • 01057cf Transform can be simplified when clone is not done. (#2621)
  • 032f096 add test for #2613 (#2618)
  • 4b334ed handle global constant collision with local variable after inline (#2617)
  • 8ddcbc3 compress apply() & call() of function (#2613)
  • 0b0eac1 drop property assignment to constants (#2612)

There are 42 commits in total.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

viperHTML doesn’t close `<abbr>` elements

viperHTML`prefix <abbr>anything</abbr> postfix` returns prefix <abbr>anything postfix without the closing </abbr>, which causes <abbr> to span the rest of the node including  postfix, rather than just anything.

Testing the same with hyperHTML`prefix <abbr>anything</abbr> postfix` correctly returns prefix <abbr>anything</abbr> postfix

Explicit escaping vs HTML

I'm considering to use viperHTML in one of my projects, however I'm a really worried about the heuristic about which content is treated as HTML and which isn't, since it is very easy to accidentally create an XSS vulnerability.

Is there a way to make that choice explicit and always treat content as escaped text, unless marked otherwise? I would imagine something like this:

render`
  <a href="${a.href}" onclick="${a.onclick}">
    ${a.text}
    <span>${raw(a.html)}</span>
  </a>
`;

HTML turns up as String

Did as directed, put expression between >< (no space) but it turns into string. Where to look next?

Spaces around text aren’t preserved

const viperHTML = require("viperhtml");

console.log(viperHTML.wire()`Hello ${"World"}`.toString()); // prints "HelloWorld"

by the way, is it intended to have to call .toString() ? it would be nice to add it to the readme if it is

it’s most likely caused by this line that changed recently :

current.push(adoptable ? text : text.trim());

so I can workaround it like this:

const viperHTML = require("viperhtml");
viperHTML.adoptable = true;

console.log(viperHTML.wire()`Hello ${"World"}`.toString()); // prints "Hello <!--:1-->World<!--:1-->"

but it adds unnecessary comments, I only want the spaces, I’m not reusing it on the client

Also, I noticed something strange:

const viperHTML = require("viperhtml");

viperHTML.wire()`Hello ${"World"}`;

viperHTML.adoptable = true;
console.log(viperHTML.wire()`Hello ${"World"}`.toString()); // prints "Hello<!--:1-->World<!--:1-->"

it adds the comments but trims the spaces !

Make it worker friendly

Hi, I would like to use viperHTML inside a service worker, however it is not possible at the moment because of the runtime dependencies. It would be nice to have the templates compiled before they got shipped to the service worker, so that the service worker would not have to incude an HTML parser, JS and CSS minifiers. It would be also nice to not depend on the node 'crypto' module. What I am envisioning is the following

// src/templates.js
const text = 'Ugly Bugly'
const render = wire()
render`<h1>${text}</h1>`
render`<h1>${text}</h1>`

would be transpiled into

// dist/__templates_internal.js
module.exports {
  hash1: {
    chunks: ['<h1>', '</h1>'],
    updates: [
      0, // this is the index of getUpdateForHTML function reference in the updateMappings array
     // [1, key] // index of updateBoolean, followed by argument 'key' for updateBoolean function
    ]  // this assumes, that there is a map in viperHTML, like : const updateMappings = [getUpdateForHTML, updateBoolean, etc..]
  }
}

// dist/templates.js
const TEMPLATES = require('./__templates_internal')

const text = 'Ugly Bugly'
const render = wire()
render(TEMPLATES.hash1.chunks, text)
render(TEMPLATES.hash1.chunks, text)

The above transpilation step would minify the JS and CSS, and assign the right type of updates for the interpolations. viperHTML however should be capable to map the template chunks to their respecitve updates array. What do you think about this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.