Skip to content

JSX to native DOM API transpilation. 💛 <div> ⟹ document.createElement('div')!

License

Notifications You must be signed in to change notification settings

DavidArchibald/nativejsx

 
 

Repository files navigation

nativejsx Build Status Version Status

JSX to native DOM API transpilation.

Like the idea of keeping JSX around as a general-purpose templating language? nativejsx is a well-rounded JSX feature subset that makes sense within the realm of JavaScript's native DOM API.

I know. "Why all the words?" Just show you something.

Example

Here's the scenario, Capitan:

function template() {
  return (
    <div class="btn-group" role="group" aria-label="Basic example">
      <button type="button" class="btn btn-secondary" onClick={eventListener}>Left</button>
      <button type="button" class="btn btn-secondary" ref={(ref) => this.middleButton = ref}>Middle</button>
      <button type="button" class="btn btn-secondary">Right</button>
      <button type="button" class="btn btn-secondary" style={{backgroundColor: 'peachpuff'}}>Primary</button>
    </div>
  );
}

That looks awesome, right? Yeah. Now we bake it in nativejsx using nativejsx.parse:

var nativejsx = require('nativejsx');

nativejsx.parse('dat-btn-group.js', {
  declarationType: 'var',
  variablePrefix: '$$'
}).then(function(transpiledGoodness) {
  console.log(transpiledGoodness);
});

That console.log reveals the amazing native DOM API output:

function template() {
    return function () {
        var $$a = document.createElement('div');
          $$a.setAttribute('class', 'btn-group');
          $$a.setAttribute('role', 'group');
          $$a.setAttribute('aria-label', 'Basic example');
          var $$b = document.createElement('button');
            $$b.setAttribute('type', 'button');
            $$b.setAttribute('class', 'btn btn-secondary');
            $$b.addEventListener('click', eventListener);
            $$a.appendChild($$b);
              var $$c = document.createTextNode('Left');
              $$b.appendChild($$c);
          var $$d = document.createElement('button');
            $$d.setAttribute('type', 'button');
            $$d.setAttribute('class', 'btn btn-secondary');
            $$a.appendChild($$d);
              var $$e = document.createTextNode('Middle');
              ((ref) => this.middleButton = ref)($$e);
              $$d.appendChild($$e);
          var $$f = document.createElement('button');
            $$f.setAttribute('type', 'button');
            $$f.setAttribute('class', 'btn btn-secondary');
            $$a.appendChild($$f);
              var $$g = document.createTextNode('Right');
              $$f.appendChild($$g);
          var $$h = document.createElement('button');
            $$h.setAttribute('type', 'button');
            $$h.setAttribute('class', 'btn btn-secondary');
            $$h.setStyles({ backgroundColor: 'peachpuff' });
            $$a.appendChild($$h);
              var $$i = document.createTextNode('Primary');
              $$h.appendChild($$i);
        return $$a;
    }.call(this);
}
IMPORTANT NOTES:
Tested Node Versions
  • 6.10.0
Frontend Dependencies

You have two choices:

  1. Use a very tiny JavaScript file located in dist, nativejsx-prototype.js. Feel free to include it in your build steps (before any nativejsx-transpiled code runs, of course).
<script type="text/javascript" src="path/to/nativejsx-prototype.js"></script>
// or the minified version
<script type="text/javascript" src="path/to/nativejsx-prototype.min.js"></script>
  1. Enable inline usage with the API option, prototypes: 'inline'. Warning: this places setAttributes and appendChildren in every file that they are needed.

API

// (String, Object) => Promise => String
parse(fileName, options)
// (String, Object) => String
parseSync(fileName, options)
// String => String
transpile(jsx)
Options
  • declarationType: var (default), const, or let.
  • variablePrefix: Any string (defaults to $$) you can conjure up that produces a valid JavaScript variable.
  • prototypes: Either true (default) or 'inline'.
  • acorn: All acorn options are available here. Defaults to {plugins: {jsx: true}, ecmaVersion: 6, sourceType: 'module'}.

Build Tools

Development

Wish List
  • More Tests.
  • Hardened Nodal JSXExpressions.
  • Gulp, grunt, and webpack plugins.
  • Source maps.
  • Support SVG elements.
  • (Your suggestion.)
Terminology
  • AST: Abstract syntax tree.
  • Compositions: These are endgame native DOM ASTs that we plan on swapping with JSX.
  • Generators: Barebone AST node types (some are combinations of node types).
  • Transformers: Takes compositions and generators and actually completes the swapping.
  • Walkers: Sets up the state, allocates variables, and traverses JSXElements to our liking.

What the heck is appendChildren?

appendChildren helps clean up the mess JSXExpressions (the {} things) leave due to JavaScript's lack of static typing. I can't rightly tell if the expressions your fingers conjure up are going to return JSX, literals, or whatever else.

What the heck is setAttributes?

setAttributes handles the JSXSpreadAttribute expression that is in the JSX Specification. In other words, <div {...attributes}></div>, where attributes is an object containing valid HTML attribute names and values, should just work. There isn't a convenient way to do this with native DOM.

What the heck is setStyles?

setStyles takes an Object that maps keys to HTMLElement.prototype.style and sets the corresponding value. This is a reimplementation of React's fancy style attribute.

Why does it output everything in a closure?

I'm glad you stuck around to ask. Due to the imperative nature of the native DOM API, we're outputting variable allocations – you know, the "$$a" stuff. To avoid variable clobbering, our DOM goodies are tucked away into a JavaScript closure, safe and sound.

About

JSX to native DOM API transpilation. 💛 <div> ⟹ document.createElement('div')!

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 100.0%